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