Skip to content

Shared Types & GraphQL Codegen Pipeline

Objective

Create shared types package and establish GraphQL code generation pipeline for type-safe client-server communication.

Background

Shared types ensure consistency between web, mobile, and API. The codegen pipeline keeps types synchronized with the database schema.

Tasks

  • Create packages/shared structure
  • Define core business types
  • Set up GraphQL schema export from PostGraphile
  • Configure code generation for ReasonML types
  • Create utility functions for type conversions
  • Add validation functions
  • Document type usage patterns

Package Structure

packages/shared/
├── lib/
│   ├── dune
│   ├── Types.re       # Core business types
│   ├── Validation.re  # Type validation
│   ├── Converters.re  # JSON conversions
│   └── GraphQL.re     # Generated types
├── scripts/
│   └── codegen.js     # Type generation script
├── schema.graphql     # Exported from PostGraphile
├── dune-project
└── package.json

Core Types

(* lib/Types.re *)
type userId = string;
type teamId = string;
type projectId = string;

type user = {
  id: userId,
  email: string,
  name: option(string),
  createdAt: Js.Date.t,
};

type task = {
  id: string,
  title: string,
  description: option(string),
  status: taskStatus,
  priority: priority,
  assigneeId: option(userId),
  projectId: projectId,
}
and taskStatus = 
  | Todo
  | InProgress
  | Done
  | Archived
and priority =
  | Low
  | Medium
  | High
  | Critical;

Code Generation Pipeline

// scripts/codegen.js
const { generateReasonTypes } = require('./generators');

async function generate() {
  // 1. Fetch schema from PostGraphile
  const schema = await fetchSchema('http://localhost:5000/graphql');
  
  // 2. Generate ReasonML types
  const types = generateReasonTypes(schema);
  
  // 3. Write to lib/GraphQL.re
  fs.writeFileSync('lib/GraphQL.re', types);
}

Dune Configuration

(library
 (public_name melange-shared)
 (name melange_shared)
 (libraries melange.js)
 (modes melange native)
 (preprocess (pps melange.ppx)))

Acceptance Criteria

  • Types compile in both Melange and native modes
  • Code generation runs automatically
  • Types match PostGraphile schema
  • Validation functions work
  • JSON serialization/deserialization works
  • Can be imported by web and mobile packages

Scripts

{
  "scripts": {
    "codegen": "node scripts/codegen.js",
    "codegen:watch": "nodemon --watch ../api-server/schema.graphql scripts/codegen.js"
  }
}

Type Safety Features

  • Variant types for enums
  • Option types for nullable fields
  • Custom scalar handlers (Date, JSON, etc.)
  • Type-safe IDs (userId, teamId, etc.)

Testing

(* test/TypesTest.re *)
let testUserValidation = () => {
  let validUser = {id: "1", email: "test@example.com", name: None, createdAt: Js.Date.now()};
  assert(Validation.isValidUser(validUser));
};

Priority: 🟡 High

Central to type safety across the stack.

Estimated Effort: 2 days

Dependencies

  • #38 PostGraphile API (for schema export)
  • #39 GraphQL-PPX (coordination on type generation)

CI Validation

  • Types compile successfully
  • Code generation runs without errors
  • Tests pass
  • Can be imported by other packages