Skip to content

Adapters

jsorm keeps database access outside the ORM core. You choose the adapter based on where your code runs:

  • Native adapters (@jsorm/pg, @jsorm/mysql, @jsorm/sqlite) for Node/Bun runtimes with direct database access.
  • Fetch adapter (@jsorm/fetch) for edge or fetch-only runtimes that cannot open TCP sockets or spawn native binaries.

Use @jsorm/node for the CLI and production deploy/migration workflows, even when your app runtime uses @jsorm/runtime + @jsorm/fetch.

PackageKindBest for
@jsorm/pgNative TCP adapterPostgreSQL in Node services, workers, CI, and deploy jobs
@jsorm/mysqlNative TCP adapterMySQL-compatible targets such as PlanetScale from Node
@jsorm/sqliteNative in-process adapterLocal SQLite files, embedded apps, tests, and tooling
@jsorm/fetchHTTP/fetch adapterVercel Edge, Cloudflare Workers, and other fetch-only runtimes
Runtime / workflowNative adapters@jsorm/fetchRecommended choice
Node.js app serverYesYesPrefer native adapters
Bun service/runtimeYesYesPrefer native adapters when direct DB access exists
Deno runtimeSQLite onlyYesPrefer fetch unless you explicitly run SQLite
Vercel Edge / Cloudflare Workers / Netlify EdgeNoYesUse @jsorm/runtime + @jsorm/fetch
CLI, migrations, jsorm deployYesNoUse @jsorm/node + native adapter
  • your runtime can open direct PostgreSQL/MySQL connections
  • you run long-lived Node or Bun services
  • you want the default production path for jsorm deploy
  • your deploy or CI job owns migrations
  • your runtime is edge-only or fetch-only
  • you cannot open TCP sockets from request handlers
  • you can expose an HTTP SQL gateway that jsorm can call
  • you keep migrations and deploy checks in a separate Node release step
import { createJsorm, defineConnectionSource, defineJsormConfig } from '@jsorm/core';
import { pgAdapter } from '@jsorm/pg';
const main = defineConnectionSource({
adapter: pgAdapter({
name: 'main',
connectionString: process.env.DATABASE_URL!,
pool: { min: 2, max: 10 },
}),
});
export default defineJsormConfig({
connectionSources: { main },
defaults: { connectionSource: 'main' },
});
export const db = await createJsorm().init();

Use runtime-safe imports in edge bundles:

import { createJsorm, defineConnectionSource, defineJsormConfig } from '@jsorm/runtime';
import { fetchAdapter } from '@jsorm/fetch';
const main = defineConnectionSource({
adapter: fetchAdapter({
name: 'main',
dialect: 'postgres',
endpoint: process.env.SQL_HTTP_ENDPOINT!,
headers: () => ({
authorization: `Bearer ${process.env.SQL_HTTP_TOKEN!}`,
}),
}),
});
export default defineJsormConfig({
connectionSources: { main },
defaults: { connectionSource: 'main' },
});
export const db = await createJsorm().init();
EnvironmentApp runtimeAdapterProduction note
Node API / worker / queue@jsorm/core or @jsorm/nodeNativeBest default for direct DB access and release jobs
Vercel Edge@jsorm/runtime@jsorm/fetchKeep jsorm deploy in GitHub Actions or another Node step
Cloudflare Workers@jsorm/runtime@jsorm/fetchPoint the adapter at an HTTP SQL endpoint you control
Docker / CI deploy job@jsorm/nodeNativeRun pnpm exec jsorm deploy --strict --verbose before rollout

Use adapter-level health checks in runtime code:

const health = await db.healthCheck();
// { main: 'ok', analytics: 'ok' }

And in Node deploy or readiness workflows:

Terminal window
pnpm exec jsorm db:check
  • Native adapters are the default choice for deploy preflight checks.
  • Fetch adapters can still participate in db.healthCheck(), but your HTTP endpoint must be reachable, authenticated, and healthy.

Transactions are scoped to a single connection source:

await db.transaction(async (tx) => {
await tx.insert(User, {
name: 'Alice',
createdAt: new Date(),
});
});
  • Native adapters support single-database transactions directly.
  • @jsorm/fetch also supports transactions, but your HTTP SQL endpoint must implement begin, commit, and rollback.
  • Cross-database transactions are not supported.

jsorm allows registering multiple connectionSources for read/write splits or different databases. Use db.use(name) to get a lightweight scoped client, or db.with(name, fn) to scope operations within a block.

import { createJsorm, defineConnectionSource, defineJsormConfig } from '@jsorm/core';
import { pgAdapter } from '@jsorm/pg';
const main = defineConnectionSource({ /* ... */ });
const analytics = defineConnectionSource({ /* ... */ });
export default defineJsormConfig({
connectionSources: { main, analytics },
defaults: { connectionSource: 'main' },
});
export const db = await createJsorm().init();
// Uses 'main' connection by default
await db.get(User, { select: { id: true } });
// Scoped client for 'analytics' connection
const analyticsDb = db.use("analytics");
await analyticsDb.get(User, { select: { id: true } });
// Scoped block
await db.with("analytics", async (scoped) => {
await scoped.raw.execute("SELECT 1");
});
  1. Default production Node app: native adapter.
  2. Edge request path: @jsorm/runtime + @jsorm/fetch.
  3. Migrations and deploy: @jsorm/node + native adapter.
  4. Local embedded SQLite: @jsorm/sqlite.