The sql Template Tag
The sql tagged-template helper builds a parameterized statement from a template literal, turning every interpolated value into a bind parameter instead of writing it into the SQL text:
import { sql } from 'postgrejs';
const id = 1;
const request = sql`select * from users where id = ${id}`;
// request.sql === 'select * from users where id = $1'
// request.params === [1]
The tag never writes a value into the text — the resulting QueryRequest can be passed directly to connection.query():
const result = await connection.query(sql`select * from t where id = any(${[1, 3]})`);
connection.query() recognizes a QueryRequest and forwards .sql and .params for you; passing params yourself alongside a sql\`` value throws, since the statement already carries its own.
Composing fragments
A QueryRequest interpolated inside another sql\`` template is spliced in as a fragment, with its own placeholders renumbered onto the outer statement:
const filter = city ? sql`where city = ${city}` : sql``;
const request = sql`select * from t ${filter} order by id`;
sql.ident(name)
Values become parameters, but identifiers (table/column/schema names) cannot be — $1 is always a value. sql.ident() writes a name into the statement instead, quoted like PostgreSQL's own quote_ident():
await connection.query(
sql`select ${sql.ident(column)} from ${sql.ident(table)}`,
);
sql.values(data, columns?)
Builds the column list and VALUES clause of an INSERT from an object, or from an array of objects for a multi-row insert:
await connection.query(sql`insert into users ${sql.values({ id: 1, name: 'ada' })}`);
// insert into users ("id","name") values ($1,$2)
await connection.query(sql`insert into users ${sql.values([
{ id: 1, name: 'a' },
{ id: 2, name: 'b' },
])}`);
// insert into users ("id","name") values ($1,$2),($3,$4)
Pass columns to restrict and order which keys are written — always do this when data comes from outside (a request body), so a caller can't smuggle in an extra column:
sql.values(userInput, ['id', 'name']);
sql.set(data, columns?)
Builds the assignment list of an UPDATE from an object:
await connection.query(
sql`update users set ${sql.set({ city })} where id = ${id}`,
);
// update users set "city" = $1 where id = $2
As with sql.values(), pass columns to restrict what gets written when data is externally supplied.
QueryRequest.stringify(options?)
Renders the statement as plain SQL text with every parameter written in as a properly encoded, explicitly cast literal — for cases where you need a literal string rather than a parameterized call, such as passing it to execute(), which has no params option:
sql`select ${42}`.stringify(); // "select '42'::int4"
sql`select ${true}`.stringify(); // "select 't'::bool"
sql`select ${null}`.stringify(); // "select null"
sql`select ${[1, 2]}`.stringify(); // "select ARRAY['1','2']::_int4"
await connection.execute(sql`select * from accounts where id = ${accountId}`);
Each value is encoded through the same type map query() uses, so both paths agree on how a value is represented. A value with no text encoding (e.g. an unregistered type) makes stringify() throw rather than silently guess — use query() instead for values like that.
sql.ident() results contain no parameters, so stringify() on an identifier fragment just returns the quoted name unchanged.