Savvi Studio

Codegen System Overview

Purpose: Understand the codegen architecture and design
Last Updated: 2024-11-28

What is Codegen?

The codegen system automatically generates type-safe TypeScript wrappers for PostgreSQL functions by introspecting your database schema. This eliminates manual type definitions and ensures your application code stays in sync with your database.

Architecture

High-Level Flow

PostgreSQL Database
  ↓ (1) introspection
Type Catalog (src/lib/codegen/introspection/)
  ↓ (2) schema generation
Zod Schemas (src/lib/codegen/builders/)
  ↓ (3) function generation
TypeScript Wrappers (src/__generated__/)
  ↓ (4) application usage
Application Code

Key Components

1. Introspection Layer (src/lib/codegen/introspection/)

  • Queries pg_catalog for types and functions
  • Builds type dependency graphs
  • Resolves composite types, enums, domains
  • Key classes: IntrospectionBootstrap, TypeCatalog, FunctionCatalog

2. Schema Generation (src/lib/codegen/builders/)

  • Converts PostgreSQL types to Zod schemas
  • Handles base types, composites, arrays, enums
  • Generates runtime validators
  • Key classes: EnumBuilder, DomainBuilder, CompositeBuilder

3. Function Wrappers (src/lib/codegen/core/)

  • Creates TypeScript functions for SQL functions
  • Adds parameter validation
  • Types return values
  • Key classes: FunctionGenerator, InvocationGenerator

4. Output Organization (src/__generated__/)

  • Organized by PostgreSQL schema
  • Index files for easy imports
  • TypeScript path alias: @db

Generated Code Structure

Directory Layout

src/__generated__/
├── index.ts              # Main exports (all schemas)
├── README.md             # Generated code notes
├── audit/               # audit schema functions
│   ├── index.ts         # Schema exports
│   └── types.ts         # Type definitions
├── auth/                # auth schema functions
│   ├── index.ts
│   └── types.ts
├── graph/               # graph schema functions
│   ├── index.ts
│   └── types.ts
└── studio/              # studio schema functions
    ├── index.ts
    └── types.ts

Import Aliasing

Configuration (tsconfig.json):

{
  "compilerOptions": {
    "paths": {
      "@db": ["./src/__generated__/index.ts"],
      "@db/*": ["./src/__generated__/*"]
    }
  }
}

Usage:

// Import all schemas
import { studio, auth, graph } from '@db';

// Import specific schema
import { createResource, getResource } from '@db/graph';

// Import types
import type { NodeData } from '@db/graph/types';

Design Principles

1. Type Safety First

Compile-time checks:

// ✅ Valid - types enforced
await createResource(client, {
  p_type_namespace: 'test.user',
  p_external_id: 'user-1'
});

// ❌ Compile error - missing required parameter
await createResource(client, {
  p_external_id: 'user-1'
});

2. Runtime Validation

Zod validates inputs:

try {
  await createResource(client, {
    p_type_namespace: untrustedInput,
    p_data: userProvidedData
  });
} catch (error) {
  if (error instanceof ZodError) {
    console.error('Invalid input:', error.errors);
  }
}

3. Single Source of Truth

Database schema drives TypeScript types:

  • Change SQL function → run codegen → TypeScript updates
  • No manual type definitions to maintain
  • No drift between database and application

4. Developer Experience

Clean, intuitive API:

  • Named parameters (not positional)
  • Optional parameters with defaults
  • Clear error messages
  • IDE autocomplete support

Codegen Process

Step 1: Introspection

Query PostgreSQL system catalogs to discover types and functions

Step 2: Type Resolution

Resolve type dependencies using topological sort

Step 3: Schema Generation

Generate Zod schemas for all discovered types

Step 4: Function Wrapper Generation

Generate TypeScript functions with validation

Benefits

1. Eliminates Manual Work

  • No manual type definitions
  • No manual validation code
  • No manual wrapper functions

2. Prevents Runtime Errors

  • Type mismatches caught at compile time
  • Invalid inputs caught by Zod
  • Database schema changes break builds

3. Improves Developer Experience

  • Autocomplete for all functions
  • Inline documentation from SQL comments
  • Clear error messages

4. Enables Safe Refactoring

  • Change SQL function signature
  • Run codegen
  • TypeScript compiler finds all call sites
  • Update code with confidence

5. Maintains Consistency

  • Single source of truth (database)
  • No drift between layers
  • Always in sync

Integration Points

Database Layer

Requires:

  • withClient for connection management
  • PoolClient instances
  • Transaction support

See: Database Documentation

Application Layer

Provides:

  • Type-safe function calls
  • Runtime validation
  • Clean API

See: Function Wrappers

Testing

Supports:

  • Mock-friendly architecture
  • Type-safe test fixtures
  • Integration testing

See: Function Patterns

Performance Considerations

Codegen Performance

  • Introspection: ~100-500ms per schema
  • Generation: ~50-200ms per schema
  • Total: Usually under 1 second for entire project

Not a bottleneck - runs once when schema changes.

Runtime Performance

Generated code is zero-overhead:

  • Direct client.query() calls
  • Minimal Zod validation overhead
  • No abstraction layers

Limitations

What Works Well

  • ✅ Base PostgreSQL types
  • ✅ Composite types (records)
  • ✅ Enums
  • ✅ Arrays
  • ✅ Domains with simple constraints
  • ✅ User-defined functions
  • ✅ Table-returning functions

Current Limitations

  • ⚠️ Complex CHECK constraints (parsed best-effort)
  • ⚠️ Some PostgreSQL-specific types (geometric, network)
  • ⚠️ Triggers and dynamic constraints
  • ⚠️ Overloaded functions (uses first signature)

Next Steps


Key Takeaway: Codegen bridges the gap between PostgreSQL and TypeScript, providing type safety without manual work.