Adapters

Choose the right storage adapter for your environment. Covers Node FS, Bun FS, Redis, S3, Mock, and building custom adapters.

Adapters

Adapters abstract storage operations behind a unified interface. Switch from local filesystem to Redis or S3 without changing a single line of collection code.


Available Adapters

AdapterRuntimeStorageUse Case
NodeFsAdapterNode.js 18+Local filesystemServer-side apps, local dev
BunFsAdapterBun 1.0+Local filesystemBun-native apps, maximum perf
BunRedisAdapterBun 1.0+RedisDistributed systems, multi-process
BunS3AdapterBun 1.0+AWS S3Production deployment, object storage
IgniterCollectionMockAdapterAnyIn-memory MapTesting and CI

NodeFsAdapter

The default adapter for Node.js environments. Uses node:fs/promises and fast-glob.

import { NodeFsAdapter } from '@igniter-js/collections/adapters';

const adapter = new NodeFsAdapter();

const docs = IgniterCollections.create()
  .withAdapter(adapter)
  .withBasePath(process.cwd())
  .addCollection(Posts)
  .build();

Features:

  • File watching via node:fs.watch
  • Recursive directory creation
  • Glob-based file listing via fast-glob
  • Standard POSIX file operations

BunFsAdapter

Optimized for Bun runtime. Uses Bun.file, Bun.write, and Bun.Glob for maximum performance.

import { BunFsAdapter } from '@igniter-js/collections/adapters';

const adapter = new BunFsAdapter();

const docs = IgniterCollections.create()
  .withAdapter(adapter)
  .withBasePath(process.cwd())
  .addCollection(Posts)
  .build();

Bun-only. This adapter uses Bun-native APIs and will throw if used in Node.js.


BunRedisAdapter

Stores documents in Redis for distributed access. Supports key prefixes for namespacing.

import { BunRedisAdapter } from '@igniter-js/collections/adapters';

const adapter = new BunRedisAdapter({
  url: process.env.REDIS_URL!,
  prefix: 'collections:',   // Keys: collections:posts:a1b2c3d4
});

const docs = IgniterCollections.create()
  .withAdapter(adapter)
  .addCollection(Posts)
  .build();

Options:

OptionTypeDefaultDescription
urlstringrequiredRedis connection URL
prefixstring''Key prefix for namespacing

BunS3Adapter

Stores documents in AWS S3 or any S3-compatible object storage.

import { BunS3Adapter } from '@igniter-js/collections/adapters';

const adapter = new BunS3Adapter({
  bucket: 'my-content-bucket',
  region: 'us-east-1',
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
  },
  // Optional: endpoint for S3-compatible services (MinIO, Cloudflare R2)
  endpoint: 'https://s3.amazonaws.com',
});

const docs = IgniterCollections.create()
  .withAdapter(adapter)
  .addCollection(Posts)
  .build();

Options:

OptionTypeDefaultDescription
bucketstringrequiredS3 bucket name
regionstringrequiredAWS region
credentials{ accessKeyId, secretAccessKey }requiredAWS credentials
endpointstringAWS defaultCustom S3 endpoint (MinIO, R2)

S3-Compatible Storage: Use the endpoint option to connect to MinIO, Cloudflare R2, DigitalOcean Spaces, or any S3-compatible service.


MockAdapter

In-memory adapter for testing. Tracks all operations via files and calls properties.

import { IgniterCollectionMockAdapter } from '@igniter-js/collections/adapters';

const mock = new IgniterCollectionMockAdapter();

const docs = IgniterCollections.create()
  .withAdapter(mock)
  .addCollection(Posts)
  .build();

// Run operations
await docs.posts.create({ data: { title: 'Test', author: 'Test' } });

// Inspect state
console.log(mock.files); // Map<string, string> — path → content
console.log(mock.calls); // Array of method call records

Adapter Interface

The IgniterCollectionAdapter interface defines six required methods and one optional:

interface IgniterCollectionAdapter {
  read(path: string): Promise<string | null>;
  write(path: string, content: string, options?: { ttl?: number }): Promise<void>;
  delete(path: string): Promise<void>;
  list(directory: string, pattern?: string, options?: {
    type?: 'file' | 'directory' | 'both';
  }): Promise<string[]>;
  exists(path: string): Promise<boolean>;
  mkdir(path: string): Promise<void>;
  watch?(path: string, callback: (event, filePath) => void): () => void;
}

Building a Custom Adapter

Implement the interface to add any storage backend:

import type { IgniterCollectionAdapter } from '@igniter-js/collections';

class PostgresAdapter implements IgniterCollectionAdapter {
  constructor(private db: Database) {}

  async read(path: string): Promise<string | null> {
    const row = await this.db.query(
      'SELECT content FROM documents WHERE path = $1',
      [path]
    );
    return row?.content ?? null;
  }

  async write(path: string, content: string): Promise<void> {
    await this.db.query(
      `INSERT INTO documents (path, content, updated_at)
       VALUES ($1, $2, NOW())
       ON CONFLICT (path) DO UPDATE SET content = $2, updated_at = NOW()`,
      [path, content]
    );
  }

  async delete(path: string): Promise<void> {
    await this.db.query('DELETE FROM documents WHERE path = $1', [path]);
  }

  async list(directory: string, pattern?: string): Promise<string[]> {
    const rows = await this.db.query(
      'SELECT path FROM documents WHERE path LIKE $1',
      [`${directory}%`]
    );
    return rows.map(r => r.path);
  }

  async exists(path: string): Promise<boolean> {
    const row = await this.db.query(
      'SELECT 1 FROM documents WHERE path = $1',
      [path]
    );
    return !!row;
  }

  async mkdir(_path: string): Promise<void> {
    // No-op — PostgreSQL doesn't need directories
  }
}

Next Steps