Skip to content

-- a maintained fork of Zapatos 6.6.1

Your types, cast from your database::

pgcast reads your Postgres schema and writes the TypeScript types. Its shortcuts produce SQL you can read and predict, and you can always drop down to a plain sql template. No ORM, no models, no hidden state.

npm install zapatos@npm:@pgcast/pgcast

TSYou write

ts
const 
films
= await db.
select
('films', db.
all
, {
lateral
: {
cast
: db.
select
('roles', {
film_id
: db.
parent
('id'),
}, {
order
: {
by
: 'billing',
direction
: 'ASC' },
}), }, }).
run
(
pool
);

SQLPostgres runs

sql
SELECT coalesce(jsonb_agg(result), '[]') AS result
FROM (
  SELECT to_jsonb("films".*) ||
    jsonb_build_object($1::text, "lateral_cast".result) AS result
  FROM "films"
  LEFT JOIN LATERAL (
    SELECT coalesce(jsonb_agg(result), '[]') AS result
    FROM (
      SELECT to_jsonb("roles".*) AS result
      FROM "roles"
      WHERE ("film_id" = "films"."id")
      ORDER BY "billing" ASC
    ) AS "sq_roles"
  ) AS "lateral_cast" ON true
) AS "sq_films"

values ["cast"]

::TypeScript knows

ts
const films: (films.JSONSelectable & db.LateralResult<{
  cast: db.SQLFragment<roles.JSONSelectable[], never>;
}>)[]
// that is
const films: {
  genres: string[];
  id: number;
  release_year: number;
  runtime_minutes: number;
  studio_id: number;
  tagline: string | null;
  title: string;
  cast: roles.JSONSelectable[];
}[]

Films with their cast, in one query and one round trip.Hover a dashed name to see its type.

Postgres is the source ​

Types come from pg_catalog, never written by hand. Change a column, regenerate, and the compiler finds every query that broke.

SQL you can read ​

Each shortcut writes one documented query. Log it, test it, or write it yourself in a sql template.

Values are parameters ​

Values travel as $1, $2. Names are quoted as Postgres quotes them, so a stray " cannot change your query.

Live playground ​

Write a query and see its SQL and its type as you type. Press Run, and a real Postgres answers, in your browser.