# graphile-cache

<p align="center" width="100%">
  <img height="250" src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" />
</p>

<p align="center" width="100%">
  <a href="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml">
    <img height="20" src="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml/badge.svg" />
  </a>
   <a href="https://github.com/constructive-io/constructive/blob/main/LICENSE"><img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/></a>
   <a href="https://www.npmjs.com/package/graphile-cache"><img height="20" src="https://img.shields.io/github/package-json/v/constructive-io/constructive?filename=graphile%2Fgraphile-cache%2Fpackage.json"/></a>
</p>


PostGraphile instance LRU cache with automatic cleanup when PostgreSQL pools are disposed.

## Installation

```bash
npm install graphile-cache pg-cache
```

Note: This package depends on `pg-cache` for the PostgreSQL pool management.

## Features

- LRU cache for PostGraphile instances
- Automatic cleanup when associated PostgreSQL pools are disposed
- Integrates seamlessly with `pg-cache`
- Service cache re-exported for convenience
- TypeScript support

## How It Works

When you import this package, it automatically registers a cleanup callback with `pg-cache`. When a PostgreSQL pool is disposed, any PostGraphile instances using that pool are automatically removed from the cache.

## Usage

### Basic Usage

```typescript
import { graphileCache, GraphileCache } from 'graphile-cache';
import { getPgPool } from 'pg-cache';
import { postgraphile } from 'postgraphile';

// Create a PostGraphile instance
const pgPool = getPgPool({ database: 'mydb' });
const handler = postgraphile(pgPool, 'public', {
  // PostGraphile options
});

// Cache it
const cacheEntry: GraphileCache = {
  pgPool,
  pgPoolKey: 'mydb',
  handler
};

graphileCache.set('mydb.public', cacheEntry);

// Retrieve it later
const cached = graphileCache.get('mydb.public');
if (cached) {
  // Use cached.handler
}
```

### Automatic Cleanup

The cleanup happens automatically:

```typescript
import { pgCache } from 'pg-cache';
import { graphileCache } from 'graphile-cache';

// Add entries
graphileCache.set('mydb.public', { pgPoolKey: 'mydb', ... });
graphileCache.set('mydb.private', { pgPoolKey: 'mydb', ... });

// When the pool is removed...
pgCache.delete('mydb');

// Both graphile entries are automatically cleaned up!
console.log(graphileCache.has('mydb.public')); // false
console.log(graphileCache.has('mydb.private')); // false
```

### Complete Example

```typescript
import { graphileCache, GraphileCache } from 'graphile-cache';
import { getPgPool } from 'pg-cache';
import { postgraphile } from 'postgraphile';

function getGraphileInstance(database: string, schema: string): GraphileCache {
  const key = `${database}.${schema}`;
  
  // Check cache first
  const cached = graphileCache.get(key);
  if (cached) {
    return cached;
  }
  
  // Create new instance
  const pgPool = getPgPool({ database });
  const handler = postgraphile(pgPool, schema, {
    graphqlRoute: '/graphql',
    graphiqlRoute: '/graphiql',
    // other options...
  });
  
  const entry: GraphileCache = {
    pgPool,
    pgPoolKey: database,
    handler
  };
  
  // Cache it
  graphileCache.set(key, entry);
  return entry;
}

// Use in Express
app.use((req, res, next) => {
  const { handler } = getGraphileInstance('mydb', 'public');
  handler(req, res, next);
});
```

### Graceful Shutdown

```typescript
import { closeAllCaches } from 'graphile-cache';

// This closes all caches including pg pools
process.on('SIGTERM', async () => {
  await closeAllCaches();
  process.exit(0);
});
```

## API Reference

### graphileCache

The main PostGraphile instance cache.

- `get(key: string): GraphileCache | undefined` - Get a cached instance
- `set(key: string, value: GraphileCache): void` - Cache an instance
- `has(key: string): boolean` - Check if an instance is cached
- `delete(key: string): void` - Remove an instance
- `clear(): void` - Remove all instances

### GraphileCache Interface

```typescript
interface GraphileCache {
  pgPool: pg.Pool;
  pgPoolKey: string;
  handler: HttpRequestHandler;
}
```

### closeAllCaches()

Closes all caches including the service cache, graphile cache, and all PostgreSQL pools.

### svcCache

Re-exported from `pg-cache` for convenience.

## Integration Details

The integration with `pg-cache` happens automatically when this module is imported. The cleanup callback is registered immediately, ensuring that PostGraphile instances are cleaned up whenever their associated PostgreSQL pools are disposed.

This design ensures:
- No memory leaks from orphaned PostGraphile instances
- Automatic cleanup without manual intervention
- Loose coupling between packages

---

## Education and Tutorials

 1. 🚀 [Quickstart: Getting Up and Running](https://constructive.io/learn/quickstart)
Get started with modular databases in minutes. Install prerequisites and deploy your first module.

 2. 📦 [Modular PostgreSQL Development with Database Packages](https://constructive.io/learn/modular-postgres)
Learn to organize PostgreSQL projects with pgpm workspaces and reusable database modules.

 3. ✏️ [Authoring Database Changes](https://constructive.io/learn/authoring-database-changes)
Master the workflow for adding, organizing, and managing database changes with pgpm.

 4. 🧪 [End-to-End PostgreSQL Testing with TypeScript](https://constructive.io/learn/e2e-postgres-testing)
Master end-to-end PostgreSQL testing with ephemeral databases, RLS testing, and CI/CD automation.

 5. ⚡ [Supabase Testing](https://constructive.io/learn/supabase)
Use TypeScript-first tools to test Supabase projects with realistic RLS, policies, and auth contexts.

 6. 💧 [Drizzle ORM Testing](https://constructive.io/learn/drizzle-testing)
Run full-stack tests with Drizzle ORM, including database setup, teardown, and RLS enforcement.

 7. 🔧 [Troubleshooting](https://constructive.io/learn/troubleshooting)
Common issues and solutions for pgpm, PostgreSQL, and testing.

## Related Constructive Tooling

### 📦 Package Management

* [pgpm](https://github.com/constructive-io/constructive/tree/main/pgpm/pgpm): **🖥️ PostgreSQL Package Manager** for modular Postgres development. Works with database workspaces, scaffolding, migrations, seeding, and installing database packages.

### 🧪 Testing

* [pgsql-test](https://github.com/constructive-io/constructive/tree/main/postgres/pgsql-test): **📊 Isolated testing environments** with per-test transaction rollbacks—ideal for integration tests, complex migrations, and RLS simulation.
* [pglite-test](https://github.com/constructive-io/constructive/tree/main/postgres/pglite-test): **🪶 Drop-in pgsql-test replacement backed by PGlite** — in-process Postgres, no server required, instance-per-suite isolation.
* [pgsql-seed](https://github.com/constructive-io/constructive/tree/main/postgres/pgsql-seed): **🌱 PostgreSQL seeding utilities** for CSV, JSON, SQL data loading, and pgpm deployment.
* [supabase-test](https://github.com/constructive-io/constructive/tree/main/postgres/supabase-test): **🧪 Supabase-native test harness** preconfigured for the local Supabase stack—per-test rollbacks, JWT/role context helpers, and CI/GitHub Actions ready.
* [graphile-test](https://github.com/constructive-io/constructive/tree/main/graphile/graphile-test): **🔐 Authentication mocking** for Graphile-focused test helpers and emulating row-level security contexts.
* [pg-query-context](https://github.com/constructive-io/constructive/tree/main/postgres/pg-query-context): **🔒 Session context injection** to add session-local context (e.g., `SET LOCAL`) into queries—ideal for setting `role`, `jwt.claims`, and other session settings.

### 🧠 Parsing & AST

* [pgsql-parser](https://www.npmjs.com/package/pgsql-parser): **🔄 SQL conversion engine** that interprets and converts PostgreSQL syntax.
* [libpg-query-node](https://www.npmjs.com/package/libpg-query): **🌉 Node.js bindings** for `libpg_query`, converting SQL into parse trees.
* [pg-proto-parser](https://www.npmjs.com/package/pg-proto-parser): **📦 Protobuf parser** for parsing PostgreSQL Protocol Buffers definitions to generate TypeScript interfaces, utility functions, and JSON mappings for enums.
* [@pgsql/enums](https://www.npmjs.com/package/@pgsql/enums): **🏷️ TypeScript enums** for PostgreSQL AST for safe and ergonomic parsing logic.
* [@pgsql/types](https://www.npmjs.com/package/@pgsql/types): **📝 Type definitions** for PostgreSQL AST nodes in TypeScript.
* [@pgsql/utils](https://www.npmjs.com/package/@pgsql/utils): **🛠️ AST utilities** for constructing and transforming PostgreSQL syntax trees.

### 📚 Documentation & Skills

* [constructive-skills](https://github.com/constructive-io/constructive-skills): **📖 Platform documentation and AI agent skills** — feature catalog, blueprint reference, SDK guides (i18n, billing, limits, events, uploads, security, entities, search, AI), and deployment guides.

Install skills for AI coding agents:

```bash
# All platform skills (security, blueprints, codegen, billing, etc.)
npx skills add constructive-io/constructive-skills

# Individual repo skills (pgpm, testing, CLI, search, etc.)
npx skills add https://github.com/constructive-io/constructive --skill pgpm
npx skills add https://github.com/constructive-io/constructive --skill constructive-testing
```

## Credits

**🛠 Built by the [Constructive](https://constructive.io) team — creators of modular Postgres tooling for secure, composable backends. If you like our work, contribute on [GitHub](https://github.com/constructive-io).**

## Disclaimer

AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.

No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
