FaasJS
Home
  • Guide
  • Documents
  • Templates
  • Changelog
  • Ecosystem

    • Docker Images
  • Github
  • Contributing
  • Sponsor
  • Security
Home
  • Guide
  • Documents
  • Templates
  • Changelog
  • Ecosystem

    • Docker Images
  • Github
  • Contributing
  • Sponsor
  • Security
  • Curated Stack Guide
  • Application Slices Guide
  • Project Config Guide
  • File Conventions
  • Code Comments Guide
  • defineApi Guide
  • Jobs Guide
  • Testing Guide
  • React Guide
  • React Data Fetching Guide
  • React Testing Guide
  • Ant Design Guide
  • Node Utils Guide
  • Logger Guide
  • Utils Guide
  • PG Query Builder and Raw SQL Guide
  • PG Table Types Guide
  • PG Schema and Migrations Guide
  • PG Testing Guide
  • CLI and Tooling Guide
  • CRUD Patterns Guide
  • faas.yaml Configuration Specification
  • Getting Started Guide
  • HTTP Plugin Guide
  • HTTP Protocol Specification
  • JSON Guide
  • Middleware Guide
  • Naming Convention
  • Plugin Specification
  • Plugins Guide
  • Routing Mapping Specification
  • Validation Guide
  • YAML Guide

PG Table Types Guide

Use this guide when defining or updating table types with @faasjs/pg declaration merging.

Applicable Scenarios

  • Defining application table shapes for @faasjs/pg
  • Adding columns or JSONB type shapes
  • Checking that query inference flows naturally from table definitions
  • Extracting helpers that depend on TableType, ColumnName, or ColumnValue

Default Workflow

  1. Put the augmentation in an app-owned type file such as src/db/tables/<table_name>.ts, where <table_name> mirrors the DB table name in snake_case (e.g. user_roles.ts for the user_roles table). Make sure tsconfig.json includes that file before expecting inference to change.
  2. In a .d.ts file, import @faasjs/pg first, then extend Tables with declare module '@faasjs/pg'.
  3. Model each table as its runtime row shape, and keep JSON and JSONB object shapes in that merged interface.
  4. Let client.query, TableType, ColumnName, and ColumnValue infer from that source instead of forcing result types with as.
  5. Use a narrow assertion only when converting data returned by client.raw(...) or another raw boundary into an app-specific shape.
  6. Add or update expectTypeOf(...) coverage when a shared helper or package change affects inference.

Minimal Example

// src/db/tables/user_roles.ts
declare module '@faasjs/pg' {
  interface Tables {
    user_roles: {
      id: number
      name: string
      metadata: {
        age: number
        timezone?: string
      }
    }
  }
}

Rules

1. Treat Tables as the source of truth

  • Tables drives table-name inference and column-level type inference.
  • When a table shape changes, update the merged interface before adjusting query code.
  • Keep the type definition close to the application boundary that owns the table.
  • Keep the augmentation in a .d.ts or .ts file that TypeScript already includes for the app.
  • When the augmentation lives in a .d.ts, import @faasjs/pg first so TypeScript augments the real module instead of declaring a separate ambient one.
  • Define JSON and JSONB column shapes in declare module '@faasjs/pg' instead of scattering duplicate aliases elsewhere.
  • If inference does not update, check tsconfig.json include, files, and project references before adding casts.

2. Keep row shapes concrete

  • Model row fields with their actual runtime names and value shapes.
  • Prefer exact object types for JSON or JSONB columns instead of any.
  • Include optional properties only when the stored JSON shape is genuinely optional.

3. Prefer inference over assertions

  • Let typed query results flow from Tables, select(...), first(), and shared helper generics.
  • Do not use as to force query result types unless the data comes from client.raw(...) or another raw boundary.
  • If typed query code seems to need a cast, fix the table definition, selected columns, or helper signature instead.

4. Preserve the consumer extension pattern

  • App code should keep using module augmentation on Tables.
  • Do not replace declaration merging with an app-specific registry or runtime-only typing.
  • When contributing to @faasjs/pg, keep fallback behavior for untyped tables deliberate and documented.

5. Keep helper types aligned

  • TableType<T> should represent the row shape for a known table.
  • ColumnName<T> should stay aligned with actual keys of the table type.
  • ColumnValue<T, C> should resolve to the value type for that column.

6. Update type coverage when the surface changes

  • Add or update expectTypeOf(...) assertions when changing declaration merging, shared helpers, or query inference.
  • If a new query-builder feature affects result shape, test both runtime output and inferred types.
  • Prefer targeted type assertions over broad snapshots of unrelated inferred types.

See Also

  • PG Query Builder and Raw SQL Guide — building typed queries against these table definitions
  • PG Schema and Migrations Guide — creating tables in migrations
  • PG Testing Guide — testing with PgVitestPlugin()

Review Checklist

  • Tables contains the new or changed table shape
  • the augmentation file lives in a source or type root included by tsconfig.json
  • JSON and JSONB columns use concrete object types defined in declare module '@faasjs/pg'
  • typed query results do not rely on as outside raw boundaries
  • declaration merging still works from consumer code
  • helper types stay aligned with the merged table definition
  • public or shared type changes include expectTypeOf(...) coverage