Skip to content

Best practices

Use jsorm with explicit runtime boundaries and predictable startup rules. The safest production setup keeps database ownership, adapter lifecycle, and deploy responsibilities separate by environment.

  • Use @jsorm/core or @jsorm/node with native adapters in Node runtimes.
  • Use @jsorm/runtime with @jsorm/fetch only in fetch-only or edge runtimes.
  • Keep CLI, migrations, and jsorm deploy in a separate Node environment.

If config is already in memory, prefer explicit initialization instead of relying on filesystem discovery:

import { createJsorm, defineConnectionSource, defineJsormConfig } from '@jsorm/core';
import { pgAdapter } from '@jsorm/pg';
const main = defineConnectionSource({
adapter: pgAdapter({
name: 'main',
connectionString: process.env.DATABASE_URL!,
}),
});
const config = defineJsormConfig({
connectionSources: { main },
defaults: { connectionSource: 'main' },
});
export const db = await createJsorm().init(config);

Use this pattern when:

  • your platform does not expose jsorm.config.ts on disk
  • you want startup to fail fast with known config
  • you need one shared bootstrap path across API servers, workers, and scripts

createJsorm().configure(config) is also valid when you want to inject config before first use.

Create runtime instances at process or module startup, not inside every request handler. Compatible runtimes reuse low-level adapter pools and worker resources, but per-request initialization still adds avoidable overhead.

Recommended pattern:

  • bootstrap once
  • reuse the same runtime in request handlers, jobs, or service modules
  • call db.close() during graceful shutdown
  • for testing: use createJsormTest() to get an isolated sandbox instead of reusing the application runtime

This keeps connection pools warm, avoids repeated worker startup, and makes lifecycle ownership obvious.

4. Separate readiness from request traffic

Section titled “4. Separate readiness from request traffic”

Initialize jsorm before serving traffic when possible. Treat database readiness as an operational concern, not as a surprise during the first customer request.

Recommended approach:

  1. initialize the runtime during app startup
  2. run a lightweight readiness probe that confirms config and connectivity
  3. expose health checks that fail when the runtime cannot reach its configured source
  4. close the runtime during shutdown so pools and workers drain cleanly

For short-lived platforms, keep initialization deterministic and keep probes lightweight.

Run jsorm deploy from CI, a release job, or another controlled Node environment:

Terminal window
pnpm exec jsorm deploy --strict --verbose

This remains true even when the application runtime is edge-only. Request handlers should execute queries; deployment jobs should own migrations and schema verification.

PitfallWhy it hurtsSafer choice
Creating a new runtime on every requestRepeats startup work and fragments lifecycle controlInitialize once and reuse
Using native adapters in edge isolatesEdge runtimes usually cannot use raw TCP or native binariesUse @jsorm/runtime + @jsorm/fetch
Expecting @jsorm/fetch to run migrationsFetch transport is not a migration ownerRun deploys from @jsorm/node in Node
Relying on disk config in restricted runtimesFilesystem discovery may be unavailable or undesirablePass config explicitly with init(config) or configure(config)
Forgetting shutdown cleanupPools and worker processes may stay open longer than intendedCall db.close() during graceful shutdown
  • Pick packages that match the real runtime boundary.
  • Initialize jsorm explicitly when config already exists in memory.
  • Reuse runtime instances instead of recreating them per request.
  • Add readiness checks before sending live traffic.
  • Keep jsorm deploy and migrations in Node-owned release workflows.