Getting started
Define a typed table, build one parameterized query, and inspect the exact SQL before connecting a driver.
Install Qubu
Add the package to a TypeScript project:
pnpm add qubu
Import query-building functions from the package root. Qubu does not need a database connection to construct or render a query.
The examples use the same order as the rendered statement: projection, FROM,
then WHERE, ordering, grouping, and pagination. select() still accepts
independent clauses in any order, which lets reusable values be composed, but
keeping the final call in SQL order makes the query easy to scan and repair.
Define a table
Use table() once for each query-facing table. Column helpers describe the
application values that can be selected and, for mutations, written.
import { integer, table, text } from "qubu"
const users = table("users", {
id: integer(),
name: text(),
email: text({ nullable: true }),
})
users.id, users.name, and users.email are typed column expressions. The
nullable email column is inferred as string | null when selected.
Build and render a query
Pass a named projection and the final clauses to select() in SQL order. Qubu
also accepts independent clause values in another order when composition needs
it, then renders the normalized statement in SQL order. The example uses the
users table from the previous section.
import { eq, from, render, select, where } from "qubu"
const query = select(
{
id: users.id,
displayName: users.name,
},
from(users),
where(eq(users.id, 7)),
)
const statement = render(query)
The default dialect quotes identifiers with double quotes and uses ? for
parameters:
statement.text
// SELECT "users"."id" AS "id", "users"."name" AS "displayName" FROM "users" WHERE ("users"."id" = ?)
statement.parameters
// [7]
The selected row type is available on the query value:
type UserRow = typeof query.row
// { id: number; displayName: string }
Note
Rendering produces a statement; it does not execute it. Keep the
RenderedQuery value for logging or testing. Bind an
application-owned adapter with qubu(), or use
execute() and executeRows() directly, to run the query.
Next steps
- Build a
SELECTwith joins, predicates, aggregates, and pagination. - Compose queries from CTEs and derived sources.
- Write mutations with typed insert/update/delete inputs.
- Choose a database dialect when the driver expects different identifier, placeholder, or pagination syntax.
- Read JSON scalars from a JSON column.
- Use the Vite compiler hint for directive-based imports.