Extension Backend Selection

Extension Backend Selection

rill extensions (fs, kv) from rill-ext provide JSON/filesystem backends. Separate packages offer alternative storage backends. Scripts use the same API regardless of backend — hosts swap backends without changing script code.

Backend Selection Strategy

Deploymentfs Backendkv BackendRationale
DevelopmentJSON (@rcrsr/rill-ext-fs)JSON (@rcrsr/rill-ext-kv)Zero configuration, file-based persistence
Single-serverLocal files (@rcrsr/rill-ext-fs)SQLite (@rcrsr/rill-ext-kv-sqlite)Drop-in database file, concurrent safe
Multi-serverS3 (@rcrsr/rill-ext-fs-s3)Redis (@rcrsr/rill-ext-kv-redis)Shared state, distributed access
Cloud/serverlessS3 (@rcrsr/rill-ext-fs-s3)Redis (@rcrsr/rill-ext-kv-redis)Cross-server access, managed services

API Contract Guarantee

All kv backends implement KvExtensionContract from @rcrsr/rill-ext-kv. All fs backends implement FsExtensionContract from @rcrsr/rill-ext-fs. Contract properties use ApplicationCallable from @rcrsr/rill. Scripts import no backend-specific types — the same script runs unchanged across JSON, SQLite, Redis, S3 backends.

# Works with ANY kv backend (JSON, SQLite, Redis)
kv.set("user", "name", "Alice")
kv.get("user", "name")   # => "Alice"

# Works with ANY fs backend (local, S3)
fs.write("/data/file.txt", "content")
fs.read("/data/file.txt")   # => "content"

Mount Configuration Examples

Development: JSON-backed kv

import { createKvExtension } from '@rcrsr/rill-ext-kv';
import { createFsExtension } from '@rcrsr/rill-ext-fs';

const kv = createKvExtension({
  mounts: {
    user: { store: './data/user.json' },
    cache: { store: './data/cache.json' }
  }
});
const fs = createFsExtension({
  mounts: {
    data: { path: './data', mode: 'read-write' }
  }
});

const ctx = createRuntimeContext({
  variables: { kv: kv.value, fs: fs.value },
});

Single-server: SQLite kv + Local fs

import { createSqliteKvExtension } from '@rcrsr/rill-ext-kv-sqlite';
import { createFsExtension } from '@rcrsr/rill-ext-fs';

const kv = createSqliteKvExtension({
  mounts: {
    user: {
      mode: 'read-write',
      database: './data/app.db',
      table: 'user_state',
      schema: {
        name: { type: 'string', default: '' },
        count: { type: 'number', default: 0 }
      }
    },
    cache: {
      mode: 'read-write',
      database: './data/cache.db',
      table: 'cache_entries'
    }
  }
});
const fs = createFsExtension({
  mounts: {
    data: { path: './data', mode: 'read-write' }
  }
});

const ctx = createRuntimeContext({
  variables: { kv: kv.value, fs: fs.value },
});

Multi-server: Redis kv + S3 fs

import { createRedisKvExtension } from '@rcrsr/rill-ext-kv-redis';
import { createS3FsExtension } from '@rcrsr/rill-ext-fs-s3';

const kv = createRedisKvExtension({
  url: 'redis://localhost:6379',
  mounts: {
    user: {
      mode: 'read-write',
      prefix: 'app:user:',
      schema: {
        name: { type: 'string', default: '' },
        count: { type: 'number', default: 0 }
      }
    },
    cache: {
      mode: 'read-write',
      prefix: 'app:cache:',
      ttl: 3600  // 1 hour expiry
    }
  },
  writePolicy: 'immediate'
});
const fs = createS3FsExtension({
  mounts: {
    data: {
      mode: 'read-write',
      region: 'us-east-1',
      bucket: 'my-app-data',
      prefix: 'documents/',
      credentials: {
        accessKeyId: process.env.AWS_ACCESS_KEY_ID,
        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
      }
    }
  }
});

const ctx = createRuntimeContext({
  variables: { kv: kv.value, fs: fs.value },
});

Backend Package Imports

Install and import external backends as separate packages:

SQLite kv backend:

npm install @rcrsr/rill-ext-kv-sqlite
import { createSqliteKvExtension } from '@rcrsr/rill-ext-kv-sqlite';

Redis kv backend:

npm install @rcrsr/rill-ext-kv-redis
import { createRedisKvExtension } from '@rcrsr/rill-ext-kv-redis';

S3 fs backend:

npm install @rcrsr/rill-ext-fs-s3
import { createS3FsExtension } from '@rcrsr/rill-ext-fs-s3';

Swapping Backends Without Script Changes

Scripts reference mount names and functions, never backend-specific configuration:

// Development backend (JSON)
const devKv = createKvExtension({
  mounts: { user: { store: './dev.json' } }
});
const devCtx = createRuntimeContext({
  variables: { kv: devKv.value },
});

// Production backend (Redis)
const prodKv = createRedisKvExtension({
  url: process.env.REDIS_URL,
  mounts: { user: { mode: 'read-write', prefix: 'prod:user:' } }
});
const prodCtx = createRuntimeContext({
  variables: { kv: prodKv.value },
});

// Same script works with both contexts
const script = `
  kv.set("user", "name", "Alice")
  kv.get("user", "name")
`;

const devResult = await execute(parse(script), devCtx);   // Uses JSON
const prodResult = await execute(parse(script), prodCtx); // Uses Redis