Raw SQL
For a query the typed methods cannot express, write the SQL yourself. Values in the template stay bound parameters.
const stats = await db.raw<{ day: Date; count: number }>`
SELECT DATE_TRUNC('day', created_at) AS day, COUNT(*)::int AS count
FROM posts WHERE org_id = ${orgId}
GROUP BY day ORDER BY day
`;
// SQL text built at runtime: $n placeholders, values after
const table = 'audit_' + year; // from your own code, never from a request
const rows = await db.rawUnsafe(`SELECT * FROM ${table} WHERE actor_id = $1`, actorId);- Every
${}indb.rawanddb.sqlis a bound parameter. They take only a real tagged template. rawUnsafeescapes nothing in its text. Never let untrusted input reach it.- Write
$1,$2on every engine; Turbine rewrites them for SQLite, MySQL and SQL Server. - Raw SQL emits
$on('query')events but skips$usemiddleware.
Typed raw SQL: db.sql<T>#
const users = await db.sql<{ id: number; name: string }>`SELECT id, name FROM users WHERE org_id = ${orgId}`;
const user = await db.sql<{ id: number; name: string }>`SELECT id, name FROM users WHERE id = ${42}`.one();
const total = await db.sql<{ count: number }>`SELECT COUNT(*)::int AS count FROM users`.scalar();Awaiting returns T[]. .one() returns the first row or null; .scalar() the first column of the first row, or null.
See also#
- Reading and writing: the typed methods to reach for first.
- Observability: raw statements in
$on('query')events.