Dyrected
Adapters

Database Adapters

Connect Dyrected to your preferred database.

Dyrected is database-agnostic. Choose the adapter that fits your infrastructure and pass it to defineConfig as db. All adapters implement the same DatabaseAdapter interface, so your application code never changes when you switch.

On Dyrected Cloud, no database adapter is needed. The database is fully managed — just omit the db field from your config and run dyrected push. Learn more →


Supported Adapters

Best choice for production. Uses postgres (node-postgres) under the hood with full JSONB support.

import { PostgresAdapter } from '@dyrected/db-postgres'

export default defineConfig({
  db: new PostgresAdapter({
    url: process.env.DATABASE_URL,  // postgresql://user:pass@host:5432/db
  }),
})

Connection pool config:

new PostgresAdapter({
  url: process.env.DATABASE_URL,
  max: 10,       // maximum pool size (default: 10)
  idle: 20_000,  // ms before an idle connection is closed (default: 20s)
  connect: 30,   // connection timeout in seconds (default: 30)
})

Vercel / serverless: Set max: 1 to avoid exhausting the connection pool across short-lived function instances.


SQLite — @dyrected/db-sqlite

Great for local development, single-server deployments, and edge environments. Uses better-sqlite3.

import { SqliteAdapter } from '@dyrected/db-sqlite'

export default defineConfig({
  db: new SqliteAdapter({
    filename: 'dyrected.db',   // path to the SQLite file
  }),
})

Note: SQLite is not suitable for serverless deployments (Vercel, Cloudflare Workers) because the filesystem is ephemeral. Use PostgreSQL instead.


MongoDB — @dyrected/db-mongodb

For document-based workloads. Uses the official MongoDB Node.js driver.

import { MongoAdapter } from '@dyrected/db-mongodb'

export default defineConfig({
  db: new MongoAdapter({
    url: process.env.MONGODB_URI,  // mongodb+srv://...
  }),
})

MySQL — @dyrected/db-mysql (coming soon)

MySQL / PlanetScale support is planned. The package is reserved but not yet implemented. Use PostgreSQL or SQLite in the meantime.


Schema sync

On startup, Dyrected calls db.sync() to ensure the required tables or collections exist. This is non-destructive — it creates missing tables but never drops or alters existing columns.

// Called automatically by createDyrectedApp(). No manual call needed.
await config.db.sync(config.collections, config.globals)

Production note: sync() is safe to run on every deploy. It is equivalent to CREATE TABLE IF NOT EXISTS — no data is ever lost.


Migrations

Dyrected does not have a built-in migration runner yet. When you add a new field to a collection, the underlying JSON/JSONB column approach means new fields appear automatically in new documents. However, if you need to add a SQL column (e.g., for a native index), handle it with your preferred migration tool:

  • PostgreSQL: node-pg-migrate, Flyway, or raw ALTER TABLE scripts
  • SQLite: better-sqlite3 with manual ALTER TABLE
  • MongoDB: No schema migration required — add fields to your config and they appear in new documents

DatabaseAdapter interface

Implement this interface to support any database. All methods are async.

import type { DatabaseAdapter, PaginatedResult } from '@dyrected/core'

class MyCustomAdapter implements DatabaseAdapter {
  // Required: list documents
  async find(args: {
    collection: string
    where?: Record<string, any>
    limit?: number
    page?: number
    sort?: string
  }): Promise<PaginatedResult> { ... }

  // Required: single document by ID
  async findOne(args: { collection: string; id: string }): Promise<any> { ... }

  // Required: insert a document
  async create(args: { collection: string; data: any }): Promise<any> { ... }

  // Required: partial update by ID
  async update(args: { collection: string; id: string; data: any }): Promise<any> { ... }

  // Required: delete by ID
  async delete(args: { collection: string; id: string }): Promise<any> { ... }

  // Required: fetch a global singleton
  async getGlobal(args: { slug: string }): Promise<any> { ... }

  // Required: upsert a global singleton
  async updateGlobal(args: { slug: string; data: any }): Promise<any> { ... }

  // Optional: create tables/collections on startup
  async sync?(collections: CollectionConfig[], globals: GlobalConfig[]): Promise<void> { ... }

  // Optional: raw query execution
  async execute?(query: string, params?: any[]): Promise<any> { ... }
}

PaginatedResult shape

interface PaginatedResult {
  docs: any[]
  total: number
  limit: number
  page: number
  totalPages: number
  hasNextPage: boolean
  hasPrevPage: boolean
}

On this page