Quickback Docs
Define

Validation

Field validation rules for Quickback definitions. Enforce data integrity with built-in validators.

Action Input Validation

You can validate input data in custom actions using Zod schemas. The input is validated before the action handler executes:

// quickback/features/applications/actions/advanceStage.ts
import { z } from 'zod';
import { defineAction } from '../.quickback/define-action';

export default defineAction({
  description: "Move application to the next pipeline stage",
  input: z.object({
    stage: z.enum(["screening", "interview", "offer", "hired"]),
    notes: z.string().min(1).max(500).optional(),
  }),
  access: { roles: ["owner", "hiring-manager"] },
  execute: async ({ db, record, ctx, input }) => {
    // input is validated against the Zod schema before execution
    return record;
  },
});

If validation fails, the API returns a 400 Bad Request with details about which fields failed.

See Actions for the full action definition reference.

Column Constraints

Field-level rules live on the column, not in a separate block. q.text({ minLength, maxLength, pattern, email }), q.enum([...]), q.url({ maxLength }) and the numeric column types carry their constraints into the Zod schema of every generated create, update, and batch route, into the OpenAPI document, and into the CMS form. See Schema for the full list.

A raw Drizzle table (sqliteTable / pgTable wrapped in defineTable) has no column-level rules. Validate those in a triggers.beforeInsert / beforeUpdate handler that throws, or convert the table to q.table().

// features/agendaGroups/agendaGroups.ts
import { feature, q } from '@quickback/compiler';

export default feature('agendaGroups', {
  columns: {
    id:             q.id(),
    name:           q.text({ maxLength: 200 }).required(),
    selectionRule:  q.enum(['one', 'multi']).default('one').required(),
    organizationId: q.scope('organization'),
    ...q.audit(),
    ...q.softDelete(),
  },
  read:   { access: { roles: ['member'] } },
  create: { access: { roles: ['member'] } },
  update: { access: { roles: ['member'] } },
  delete: { access: { roles: ['member'] }, mode: 'soft' },
});

There is no `validation:` key

A top-level validation: { field: { maxLength, enum } } block on defineTable() is a compile error, as is any other key the compiler does not understand. Earlier compilers let unknown keys through silently, so a validation: block looked like it limited a field while the generated routes accepted anything. Move each rule onto its column.

On this page