Extension Backend Selection

Extension Backend Selection

rill provides core extensions (fs, kv) with built-in JSON/filesystem backends and separate packages for 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 (core)JSON (core)Zero configuration, file-based persistence
Single-serverLocal files (core)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 interface. All fs backends implement FsExtensionContract interface. 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")
# Result: "Alice"

# Works with ANY fs backend (local, S3)
fs::write("data", "file.txt", "content")
fs::read("data", "file.txt")
# Result: "content"

Mount Configuration Examples

Development: JSON-backed kv

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

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

Single-server: SQLite kv + Local fs

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

const ctx = createRuntimeContext({
  functions: {
    ...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'
        }
      }
    }).functions,
    ...createFsExtension({
      mounts: {
        data: { path: './data', mode: 'read-write' }
      }
    }).functions,
  },
});

Multi-server: Redis kv + S3 fs

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

const ctx = createRuntimeContext({
  functions: {
    ...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'
    }).functions,
    ...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
          }
        }
      }
    }).functions,
  },
});

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 devCtx = createRuntimeContext({
  functions: {
    ...createKvExtension({
      mounts: { user: { store: './dev.json' } }
    }).functions,
  },
});

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

// 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