---
title: Never Hand-Edit `database.types.ts` — Regenerate It
impact: HIGH
impactDescription: prevents silent type/schema drift
tags: tenant, supabase, typescript, codegen
---

## Never Hand-Edit `database.types.ts` — Regenerate It

`packages/supabase/src/database.types.ts` is generated by `pnpm supabase:web:typegen` from the live database schema. Hand-editing it makes the types lie: the compiler signs off on a query shape that the database will reject (or worse, accept with unexpected nulls). Always regenerate after a migration, commit the diff alongside the SQL change, and use `Tables<'name'>` for type references so consumers pick up changes automatically.

**Incorrect (hand-add a column to the type definition):**

```ts
// packages/supabase/src/database.types.ts
// "Just added the column to the type — I'll run the migration later."
export interface Database {
  public: {
    Tables: {
      projects: {
        Row: {
          id: string;
          account_id: string;
          name: string;
          archived: boolean;     // <-- hand-added; not in the DB yet
          created_at: string;
        };
        // Insert/Update types don't match the new field either.
      };
    };
  };
}

// Code that reads `project.archived` compiles fine.
// At runtime, every row returns archived: undefined.
// First user to hit the toggle: NotNullViolation from Postgres.
```

**Correct (write the migration, regenerate, commit both):**

```bash
# 1. Add the column in the schema file.
#    apps/web/supabase/schemas/20-projects.sql
#    alter table public.projects add column archived boolean not null default false;

# 2. Reset the local DB so the schema files re-apply (if developing locally).
pnpm supabase:web:reset

# 3. Regenerate types from the live schema.
pnpm supabase:web:typegen

# 4. Verify policies still pass.
pnpm supabase:web:test

# 5. Commit the SQL change AND the regenerated types together.
git add apps/web/supabase/schemas/20-projects.sql \
        packages/supabase/src/database.types.ts
```

**Correct (use `Tables<'name'>` for type references — auto-updates with regen):**

```ts
import { Database, Tables } from '@kit/supabase/database';

// `Tables<'projects'>` is `Database['public']['Tables']['projects']['Row']`.
// New columns appear automatically the next time typegen runs.
type Project = Tables<'projects'>;

// For inserts/updates, use the generated insert/update types:
type ProjectInsert = Database['public']['Tables']['projects']['Insert'];
type ProjectUpdate = Database['public']['Tables']['projects']['Update'];

function createProject(data: ProjectInsert) {
  // Insert type knows which columns have defaults and which don't.
}
```

**Incorrect (hand-roll your own row type — drifts on every schema change):**

```ts
// This will be wrong as soon as a migration adds a column.
interface Project {
  id: string;
  account_id: string;
  name: string;
}
```

**When you really need a customised shape:** define a derived type, never a parallel one.

```ts
type ProjectSummary = Pick<Tables<'projects'>, 'id' | 'name' | 'archived'>;
type ProjectWithOwner = Tables<'projects'> & { owner: Tables<'accounts'> };
```

**Why this isn't bureaucracy:** the type file is ~10k lines of generated JSON-ish noise. Diffing a hand-edit against a typegen pass is impossible in code review — the change either rolls forward cleanly with the migration, or it gets lost. Treat it like a build artifact that happens to be checked in.

Reference: [Supabase TypeScript types](https://supabase.com/docs/guides/api/rest/generating-types)
