Sequelize is a mature ORM for SQL databases with model definitions, associations, transactions, and broad driver support. It is a strong fit when your team prefers an Active Record style API and deploys DaloyJS on Node.js.
One request through Sequelize
01clientHTTP requestGET /users/:id
02zodValidated inputparams.id is a uuid
03sequelizeModel querystate.db.User.findByPk(id)
04responseTyped bodyuser.toJSON() | 404
Zod validates the request, the handler queries a Sequelize model off state.db, then the response schema checks the body on the way out (call toJSON() so the plain object matches the schema).
// src/db/plugin.tsimport type { App } from "@daloyjs/core";import { sequelize, User } from "./sequelize.ts";export const db = { sequelize, User };export const sequelizePlugin = { name: "sequelize", async register(app: App) { await sequelize.authenticate(); app.decorate("db", db); app.onClose(async () => { await sequelize.close(); }); },};
4. Augment app state types
Add the declare module block to the same module that exports db, not to a separate .d.ts file. Declaration files are exempt from type-checking when skipLibCheck is on (the scaffolded default), so a broken import inside a .d.ts fails silently and state.db quietly degrades to any.
ts
// src/db/plugin.ts (same module as the plugin above)declare module "@daloyjs/core" { interface AppState { db: typeof db; }}
5. Use it in routes
ts
// src/server.tsimport { z } from "zod";import { App, HttpError } from "@daloyjs/core";import { serve } from "@daloyjs/core/node";import { sequelizePlugin } from "./db/plugin.ts";const UserSchema = z.object({ id: z.uuid(), email: z.email(), name: z.string().nullable(),});const app = new App();app.register(sequelizePlugin);app.get( "/users/:id", { operationId: "getUser", request: { params: z.object({ id: z.uuid() }) }, responses: { 200: { description: "Found", body: UserSchema }, 404: { description: "Not found" }, }, }, async ({ params, state }) => { const user = await state.db.User.findByPk(params.id); if (!user) { throw new HttpError(404, { title: "User not found" }); } return { status: 200, body: user.toJSON() }; },);await app.ready();serve(app, { port: 3000 });
Transactions
Use managed transactions so DaloyJS can map one handler invocation to one atomic unit of work.
Sequelize supports migrations via the CLI, but many teams keep model definitions in TypeScript and run explicit migration files through sequelize-cli or Umzug. Keep that workflow outside your request path and initialize models before calling app.ready().