Quickback Docs
Quickback for Hono API

TypeScript Type Tree

Lightweight, branded table and action types generated directly from Quickback's compiler model.

Every compile emits quickback.gen.ts: a compact, type-only tree designed for application code. It is generated directly from the same compiler model that drives routes and OpenAPI, so there is no runtime package and no code to initialize.

import type { Api, Qb } from "../quickback.gen";

type Application = Api.applications.Row;
type NewApplication = Api.applications.Insert;
type ApplicationPatch = Api.applications.Update;
type ApplicationId = Api.applications.Id;

type AdvanceInput = Api.applications.actions.advance.Input;
type AdvanceOutput = Api.applications.actions.advance.Output;
type PipelineItem = Api.applications.views.pipeline.Item;
type PipelineResponse = Api.applications.views.pipeline.Response;

type Page = Qb.Page<Application>;
type ApiProblem = Qb.Problem;

Feature areas preserve their authored hierarchy:

type Booking = Api.events.travel.bookings.Row;

Table types

Each resource table exposes:

MemberMeaning
IdNominal table-specific primary-key type
RowComplete selected wire shape
InsertCreateable fields, with defaults and nullable fields optional
UpdateUpdatable fields, all optional
ListResponseQb.Page<Row>
columns.<name>Type of one row column
views.<name>.ItemA view's projected row
views.<name>.ResponsePage plus typed aggregations where declared
actions.<name>.InputThe action's Zod input contract
actions.<name>.OutputThe declared Zod output contract, or unknown

Primary and foreign keys are branded by default. That makes accidentally passing a candidate ID where a job ID is required a TypeScript error, while both values remain ordinary strings or numbers at runtime.

function loadJob(id: Api.jobs.Id) {}

declare const candidateId: Api.candidates.Id;
loadJob(candidateId); // TypeScript error

At an untyped boundary, validate the value and cast it once:

const jobId = routeParams.id as Api.jobs.Id;

Reading collections

List and view responses share one envelope: rows under data, pagination metadata under pagination, and view naming the projection behind the response (null for the default collection read).

declare const res: Api.applications.ListResponse; // Qb.Page<Api.applications.Row>

res.data;                  // Api.applications.Row[]
res.view;                  // string | null
res.pagination.count;      // rows in this page
res.pagination.hasMore;
res.pagination.nextCursor; // pass back as ?starting_after=
res.pagination.total;      // number | undefined — only when ?total=true

total and totalPages are counted only when the request passes ?total=true, so both are optional; every other member is always present. A view that declares aggregations intersects this shape with a typed aggregations object, and an aggregate-only view returns { view, aggregations } with no rows or pagination.

Declaring action outputs

Add an optional output Zod schema to make an action end-to-end typed:

export default defineAction({
  description: "Advance an application",
  input: z.object({
    nextStatus: z.enum(["screening", "interview", "offer"]),
  }),
  output: z.object({
    id: z.string(),
    status: z.enum(["screening", "interview", "offer"]),
  }),
  access: { roles: ["recruiter+"] },
  async execute({ record, input }) {
    return { id: record.id, status: input.nextStatus };
  },
});

The generated feature helper checks execute against the declared output. The CLI harvests both schemas locally without invoking execute; the resulting contract feeds quickback.gen.ts, OpenAPI, and api-types.gen.ts.

Configuration

The artifact is on by default:

export default defineConfig({
  // Default: quickback.gen.ts
  types: true,
});

Use a custom path, fan out identical copies to colocated apps, disable branding, or turn the artifact off:

export default defineConfig({
  types: {
    output: [
      "quickback.gen.ts",
      "apps/web/src/lib/quickback.gen.ts",
    ],
    brandIds: true,
  },
});
types valueBehavior
true or omittedWrite quickback.gen.ts
falseDo not emit the first-party type tree
"src/lib/quickback.gen.ts"Write to one custom path
["quickback.gen.ts", "apps/web/src/quickback.gen.ts"]Fan out identical copies
{ output, brandIds: false }Configure paths and use structural IDs

api-types.gen.ts remains available through openapi.types for openapi-fetch and third-party OpenAPI tools. The two artifacts are independent: disabling OpenAPI types does not disable the first-party tree.

The compiler also emits reports/types.parity.gen.ts when both artifacts exist. Quickback's output-matrix CI compiles that proof separately to require mutual assignability between direct table/action types and the OpenAPI wire types; it is outside the generated application's src/**/* build.

On this page