Skip to main content

Source: ocean/docs/adr/0040-graphql-schema-first-typescript.md | ✏️ Edit on GitHub

ADR-004: GraphQL Schema-First TypeScript Code Generation

Status

Accepted

Date

2025-07-29

Context

Our GraphQL implementation was using any types throughout the codebase, leading to:

  • Type safety issues and runtime errors
  • Poor developer experience with no IntelliSense
  • Difficulty maintaining consistency between GraphQL schema and TypeScript types
  • Manual type definitions that could drift from actual GraphQL responses

We needed a systematic approach to generate TypeScript types from our GraphQL schema to ensure type safety and maintainability.

Decision

We will implement a schema-first approach using GraphQL Code Generator to automatically generate TypeScript types from our GraphQL schema.

Implementation Details

  1. GraphQL Code Generator Setup:

    • Added @graphql-codegen/cli, @graphql-codegen/typescript, and related packages
    • Created codegen.ts configuration file
    • Added pnpm run codegen script for type generation
  2. Schema Definition:

    • Created src/generated/schema.graphql as single source of truth
    • Schema mirrors the GraphQL Yoga implementation in supabase/functions/graphql-v2/index.ts
    • Includes proper scalar mappings (JSON: Record<string, any>)
  3. Type Generation:

    • Generates src/generated/graphql.ts with full TypeScript types
    • Creates src/generated/introspection.json for tooling
    • Uses Prettier for consistent formatting
  4. Type Transformation Layer:

    • Created transformation functions to map GraphQL types to Database types
    • Handles differences between GraphQL response structure and internal data models
    • Ensures proper nullability handling (organizations always have member roles)
  5. Integration Points:

    • Updated all GraphQL hooks to use generated types
    • Replaced any types with proper GraphQL-generated interfaces
    • Added ESLint exception for generated files

Alternatives Considered

1. Manual Type Definitions

  • Pros: Simple, no build step required
  • Cons: Types can drift from schema, maintenance burden, no validation

2. Runtime Schema Introspection

  • Pros: Always up-to-date with deployed schema
  • Cons: Network dependency, slower development, build complexity

3. Code-First with TypeGraphQL

  • Pros: Single source of truth in TypeScript
  • Cons: Would require rewriting existing GraphQL Yoga server, migration complexity

Consequences

Positive

  • Full Type Safety: All GraphQL operations are now fully typed
  • Developer Experience: IntelliSense and compile-time error detection
  • Maintainability: Schema changes automatically propagate to TypeScript types
  • Consistency: Single source of truth prevents type drift
  • Documentation: Generated types serve as living documentation

Negative

  • Build Step Dependency: Requires running codegen when schema changes
  • Complexity: Additional transformation layer needed for Database type compatibility
  • Generated Code: Large generated files that shouldn't be manually edited

Neutral

  • Learning Curve: Team needs to understand codegen workflow
  • Tooling Setup: One-time configuration overhead

Implementation Notes

Type Transformation Pattern

// GraphQL types (generated)
import { Organization as GraphQLOrganization } from '@/generated/graphql'

// Database types (from Supabase)
import { Database } from '@/types/supabase'

// Transform function
function transformGraphQLOrganizationToDatabase(org: GraphQLOrganization): Organization {
return {
// Map GraphQL camelCase to Database snake_case
// Handle nullability differences
// Add computed fields
}
}

Development Workflow

  1. Update GraphQL schema in src/generated/schema.graphql
  2. Run pnpm run codegen to generate types
  3. Update transformation functions if needed
  4. TypeScript compiler catches any breaking changes

Monitoring and Maintenance

  • Schema Sync: Ensure generated schema stays in sync with GraphQL Yoga server
  • Type Drift: Monitor for differences between GraphQL and Database types
  • Performance: Generated types have minimal runtime impact
  • Updates: Codegen packages should be updated regularly for latest features

References