Compose custom SQL expressions
A query needs a database expression that is not directly represented by Drizzle’s higher-level builder.
Drizzle ORM · Type Alias
Drizzle’s sql template embeds parameterized SQL fragments in type-safe queries. The sql<T> form annotates the expected result type for TypeScript, while .as() names computed fields for later reference.
The sql template builds SQL fragments while parameterizing interpolated values. Adding a generic such as sql<string> supplies a compile-time result type, and .as() gives a selected expression a usable column alias; neither performs runtime conversion.
sql<T>`...`; sql`...`.as('alias_name'); sql.raw('...')| Name | Type | Description |
|---|---|---|
T | generic | Optional TypeScript result type expected for the SQL expression; it affects static typing only. |
A SQL query fragment/expression that can be composed into Drizzle queries; with sql<T>, its compile-time result type is T.
.as() helper explicitly names a SQL expression so its result can be addressed as a field.sql.raw() only for trusted SQL because its contents bypass parameter processing and escaping.import { sql } from 'drizzle-orm';
const expression = sql<string>`lower(${users.name})`;
const query = db.select({ name: expression.as('name') }).from(users);This embeds a parameterized SQL expression, declares its expected TypeScript type, and aliases it for use as a selected field.
A query needs a database expression that is not directly represented by Drizzle’s higher-level builder.
A computed SQL value must be returned as a named field or reused by a CTE.
A filter requires an application-specific SQL predicate or operator.
sql template?The sql template lets you write SQL fragments inside Drizzle queries while interpolated values are handled as parameters. It is useful when the high-level query builder does not express a needed SQL operation.
Follow-up: What does sql<T> guarantee? It only supplies a compile-time expected type; it cannot cast or map the runtime database value.
sql<T> do?sql<T> annotates the expected TypeScript type of an SQL expression. Drizzle does not use that generic to perform runtime mapping or conversion.
Follow-up: What risk follows from an incorrect generic? The static type can disagree with the actual database value, so application behavior may be wrong at runtime.
Use .as('alias_name') on a SQL expression when the expression is selected and must be referenced later, such as from a CTE. Without an alias, Drizzle cannot provide a usable name for that field.
Follow-up: Why can this become a runtime issue? A later query has no column name through which it can reference the expression.
sql.raw()sql<T>sql``.as<T>()DrizzleTypeError