Express custom SQL
A query needs a database expression that is not directly represented by the builder.
Drizzle ORM · Function
Drizzle's sql template builds composable, type-safe SQL fragments for use inside query-builder clauses or direct execution. Schema objects are escaped and runtime interpolations are emitted as bound parameters.
The sql template creates composable SQL fragments that can be embedded in Drizzle queries or executed directly. Tables and columns are converted into escaped SQL identifiers, while interpolated runtime values become bound parameters; sql<T> only guides TypeScript and does not transform results at runtime.
sql`...${value}...`| Name | Type | Description |
|---|---|---|
template literal | TemplateStringsArray and interpolated values | SQL template chunks and interpolated Drizzle SQL wrappers, tables, columns, or runtime values. |
A composable SQL fragment; when executed, it produces SQL text plus separately bound parameters.
drizzle-orm package.import { sql } from 'drizzle-orm';
const id = 69;
const query = sql`select * from ${usersTable} where ${usersTable.id} = ${id}`;
await db.execute(query);Builds a parameterized query that filters the users table by an ID while letting Drizzle escape identifiers and bind the value.
A query needs a database expression that is not directly represented by the builder.
Only one clause requires SQL while the rest benefits from Drizzle's builder API.
A computed field needs a SQL function or expression.
A query must execute a complete parameterized SQL statement.
sql template?The sql template lets you write SQL fragments that remain composable with Drizzle's query builder. It supports partial statements such as SELECT expressions, filters, ordering, grouping, and relational-query extras.
Follow-up: How are interpolated values handled? Runtime values are converted to placeholders and sent separately as parameters, while tables and columns are escaped as identifiers.
Interpolated tables and columns are mapped to escaped SQL syntax, including qualified column names. Dynamic values such as an ID become placeholders such as $1, with their actual values stored separately.
Follow-up: What does sql<T> add? It supplies a TypeScript-only result type and performs no runtime mapping.
sql.raw() be appropriate?Use sql.raw() when deliberately inserting SQL text without Drizzle's normal processing or escaping. This is different from normal interpolation, which preserves identifier escaping and parameter binding.
Follow-up: How can generated SQL be inspected? Specify the database dialect when obtaining the query string and its parameters.
sql.raw()sql<T>db.execute()SQLWrapperDrizzle query builder