Recipes

Add Field to Entity

Step-by-step checklist for adding a new field

Add Field to Entity

TL;DR: 6 files to modify. Use this checklist.

Checklist

#FileAction
1src/lib/db/schema/<entity>.tsAdd column to table
2src/features/<entity>/validation.tsAdd to Zod schema
3src/features/<entity>/queries.tsAdd to types and select
4src/features/<entity>/actions.tsAdd to create/update
5src/server/routers/<entity>.tsVerify input schema
6UI componentsAdd to forms

After: npm run typecheck


Example: Add "priority" to Projects

Step 1: Database Schema

// src/lib/db/schema/projects.ts
export const projects = pgTable("projects", {
  // ... existing columns
  priority: text("priority").notNull().default("medium"), // ADD THIS
})
npm run db:push

Step 2: Validation Schema

// src/features/projects/validation.ts
export const createProjectSchema = z.object({
  name: z.string().min(1),
  description: z.string().optional(),
  status: z.enum(["active", "completed", "archived"]).default("active"),
  priority: z.enum(["low", "medium", "high"]).default("medium"), // ADD THIS
})

export const updateProjectSchema = createProjectSchema.partial()

Step 3: Queries

// src/features/projects/queries.ts

// Add to type
export type Project = {
  id: string
  name: string
  description: string | null
  status: string
  priority: string // ADD THIS
  createdAt: Date
  updatedAt: Date
}

// Add to select in listProjects
.select({
  id: projects.id,
  name: projects.name,
  status: projects.status,
  priority: projects.priority, // ADD THIS
  createdAt: projects.createdAt,
})

Step 4: Actions

Actions automatically include new fields via spread:

// src/features/projects/actions.ts
// No changes needed if using spread:
.values({
  organizationId: params.organizationId,
  ...params.data, // Includes priority automatically
})

Step 5: Router

Router uses Zod schema from validation.ts, so no changes needed if you updated Step 2.

Step 6: UI

// In your form component
<Select value={values.priority} onValueChange={(v) => setField("priority", v)}>
  <SelectTrigger>
    <SelectValue placeholder="Priority" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="low">Low</SelectItem>
    <SelectItem value="medium">Medium</SelectItem>
    <SelectItem value="high">High</SelectItem>
  </SelectContent>
</Select>

Verify

npm run typecheck

If TypeScript passes, you're done.


Common Mistakes

Wrong: Adding column but forgetting Zod schema

// DB has the column, but validation rejects it
// Error: "Unrecognized key: priority"

Correct: Always update validation.ts first


Wrong: Adding nullable column without default

priority: text("priority").notNull(), // Will fail for existing rows

Correct: Add default for existing data

priority: text("priority").notNull().default("medium"),

On this page