Best practices
Operational best practices
Section titled “Operational 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.
1. Respect runtime boundaries
Section titled “1. Respect runtime boundaries”- Use
@jsorm/coreor@jsorm/nodewith native adapters in Node runtimes. - Use
@jsorm/runtimewith@jsorm/fetchonly in fetch-only or edge runtimes. - Keep CLI, migrations, and
jsorm deployin a separate Node environment.
2. Configure and initialize explicitly
Section titled “2. Configure and initialize explicitly”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.tson 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.
3. Reuse adapters and runtime resources
Section titled “3. Reuse adapters and runtime resources”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:
- initialize the runtime during app startup
- run a lightweight readiness probe that confirms config and connectivity
- expose health checks that fail when the runtime cannot reach its configured source
- close the runtime during shutdown so pools and workers drain cleanly
For short-lived platforms, keep initialization deterministic and keep probes lightweight.
5. Keep deploy ownership in Node
Section titled “5. Keep deploy ownership in Node”Run jsorm deploy from CI, a release job, or another controlled Node environment:
pnpm exec jsorm deploy --strict --verboseThis remains true even when the application runtime is edge-only. Request handlers should execute queries; deployment jobs should own migrations and schema verification.
6. Common pitfalls
Section titled “6. Common pitfalls”| Pitfall | Why it hurts | Safer choice |
|---|---|---|
| Creating a new runtime on every request | Repeats startup work and fragments lifecycle control | Initialize once and reuse |
| Using native adapters in edge isolates | Edge runtimes usually cannot use raw TCP or native binaries | Use @jsorm/runtime + @jsorm/fetch |
Expecting @jsorm/fetch to run migrations | Fetch transport is not a migration owner | Run deploys from @jsorm/node in Node |
| Relying on disk config in restricted runtimes | Filesystem discovery may be unavailable or undesirable | Pass config explicitly with init(config) or configure(config) |
| Forgetting shutdown cleanup | Pools and worker processes may stay open longer than intended | Call db.close() during graceful shutdown |
Production checklist
Section titled “Production checklist”- 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 deployand migrations in Node-owned release workflows.