Getting started
This guide walks through a complete example: setting up jsorm.config.ts, defining models, inserting records, and running typed queries.
-
Define models
Import
defineModeland the field buildert. The same definition drives both TypeScript types and runtime query behavior.// src/schema/main/user.tsimport { defineModel, t } from 'jsorm';export const Role = defineModel('roles', {id: t.number().primary(),name: t.string().unique(),});export const User = defineModel('users', {id: t.number().primary(),name: t.string(),email: t.string().optional(),active: t.boolean().default(true),createdAt: t.date(),role: t.belongsTo(Role),});Export inferred types when you need them elsewhere:
import type { InferModel, InferInput } from 'jsorm';type UserRecord = InferModel<typeof User>;// { id: number; name: string; email?: string; active: boolean; createdAt: Date; role: RoleRecord }type UserInput = InferInput<typeof User>;// { name: string; email?: string; active?: boolean; createdAt: Date; role: { connect: number } | ... } -
Create
jsorm.config.tsOne config file, used by both the runtime singleton and the CLI. Run
jsorm initto generate it, or write it manually:// jsorm.config.tsimport { defineConnectionSource, defineJsormConfig } from 'jsorm';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',},});The global
jsormsingleton lazy-loads this file automatically fromprocess.cwd()on first use. -
Insert data
Import
jsormand calljsorm.insert(). Relations can be connected or created inline.import { jsorm } from 'jsorm';import { User } from './src/schema/index.js';// Connect to an existing role by IDawait jsorm.insert(User, {name: 'Alice',email: 'alice@example.com',createdAt: new Date(),role: { connect: 1 },});// Create a new role and user togetherawait jsorm.insert(User, {name: 'Bob',createdAt: new Date(),role: { create: { name: 'editor' } },}); -
Read data
Use
jsorm.get(). Every field and relation you select is typed exactly.const users = await jsorm.get(User, {select: {id: true,name: true,email: true,role: { name: true },},where: {active: true,role: { name: { eq: 'admin' } },},orderBy: [{ name: 'asc' }],pagination: {perPage: 10,currentPage: 1,},});// users.data: Array<{ id: number; name: string; email?: string; role: { name: string } }>// users.pagination: { perPage, currentPage, total, lastPage, ... } -
Update records
Always include
whereon write operations.await jsorm.update(User, {data: { active: false },where: { role: { name: { eq: 'guest' } } },}); -
Delete records
Relation cascades follow your model’s
onDeleteconfig.await jsorm.delete(User, {where: { id: { eq: 10 } },});
Raw SQL
Section titled “Raw SQL”For queries the JSON DSL does not cover, use jsorm.executeSql(). Always use parameterized placeholders:
const result = await jsorm.executeSql( 'SELECT COUNT(*) AS total FROM users WHERE active = $1', [true],);Health check
Section titled “Health check”Use jsorm.healthCheck() to verify database connectivity — useful in readiness probes and deploy verification:
const health = await jsorm.healthCheck();// { main: 'ok' }Graceful shutdown
Section titled “Graceful shutdown”Call jsorm.close() to drain connection pools on process exit:
process.on('SIGTERM', async () => { await jsorm.close(); process.exit(0);});Best practices
Section titled “Best practices”- Import
jsormonce from'jsorm'— it’s a global singleton backed byjsorm.config.ts. - Use
jsorm.use('name')to target a non-default connection source. - Keep
whereclauses explicit on all write operations. - Prefer the typed JSON DSL; reserve
executeSqlfor genuinely custom queries. - Always close the pool on graceful shutdown.