🪨 damian

Easy migrations and typesafe queries with raw SQL

No schema DSL. No diff engine. No "push" shortcut. One workflow, from local to production.

Why Damian?

SQL as source of truth

Write SQL → get types. Not the other way around.

One workflow

No 'push', no 'migrate dev', only 'migrate'.

Query safety

Write queries with typesafe helpers and parameterization.

Installation

npm install -D damian && npm install @damiandb/pg
pnpm add -D damian && pnpm add @damiandb/pg
yarn add -D damian && yarn add @damiandb/pg

Quick Example

import { db, sql } from './db'import { UsersTable } from 'tables'const searchParams = { email: "alice@example.com" }// no, this won't cause SQL injectionconst { rows } = await db.query(sql(UsersTable)`    SELECT * FROM ${UsersTable}    WHERE ${UsersTable.email} = ${searchParams.email}`)// row is typed as { id: number, name: string, email: string }const user = rows[0]
import { db, sql } from './db'import { UsersTable } from 'tables'const { cols, row } = UsersTable.createRow({ name: "Alice", email: "alice@example.com" })await db.query(sql`    INSERT INTO ${UsersTable} ${sql.tuple(cols)}    VALUES ${sql.tuple(row)}`)
import { db, sql } from './db'import { UsersTable } from 'tables'const formInput = { name: "Alice Renamed" }await db.query(sql`    UPDATE ${UsersTable}    SET ${UsersTable.name} = ${formInput.name}    WHERE ${UsersTable.id} = ${1}`)
import { db, sql } from './db'import { PostsTable } from 'tables'const post = { id: 42 }await db.query(sql`    DELETE FROM ${PostsTable}    WHERE ${PostsTable.id} = ${post.id}`)

Get Started →