Skip to content

feat(generator): add Prisma schema generator #18

Description

@dev-queiroz

Summary

Implement the first production consumer of the Database Schema IR to validate the architecture.

Motivation

Forge recently introduced a database-agnostic Database Schema IR as an intermediate representation layer. The current pipeline is incomplete:

Contract → Semantic Model → Database Schema IR → (NO CONSUMER)

The IR architecture cannot be validated without a real generator consuming it. Prisma will be the first consumer to prove the design.

Technical Design

Architecture

The Prisma generator must consume only the DatabaseSchema IR, not the AST or Semantic Model:

Contract
    ↓
Semantic Model
    ↓
Database Schema IR ← PrismaGenerator reads ONLY from here
    ↓
schema.prisma

Implementation Structure

Create new module:

packages/generators/src/prisma/
  ├── generator.ts      (main generator class)
  ├── mapper.ts         (type mappings)
  ├── writer.ts         (file output)
  └── index.ts          (module exports)

Type Mappings

Forge → Prisma:

  • stringString
  • intInt
  • floatFloat
  • decimalDecimal
  • booleanBoolean
  • uuidString (Prisma doesn't have native UUID)
  • datetimeDateTime
  • dateDateTime

Field Modifiers

Support DatabaseField properties:

  • isPrimary@id decorator
  • isNullable → optional field (no !)
  • isUnique@unique decorator

Acceptance Criteria

  • PrismaGenerator class created in packages/generators/src/prisma/
  • Generator accepts DatabaseSchema (not SemanticModel, not AST)
  • All 8 type mappings implemented correctly
  • Primary key fields marked with @id
  • Unique fields marked with @unique
  • Nullable fields generated without ! constraint
  • Multiple contracts generate multiple models
  • Generated schema.prisma is valid Prisma syntax
  • Snapshot tests cover all type mappings
  • Unknown types throw descriptive error
  • All 29 existing tests still pass
  • Zero regressions in other generators
  • TypeScript strict mode compliance

Example Output

Input

Forge contract:

contract User {
  id: uuid
  name: string
  email: string
}

Semantic model produces these fields:

{ name: 'id', type: 'uuid', isPrimary: true, isNullable: false, isUnique: true }
{ name: 'name', type: 'string', isPrimary: false, isNullable: false }
{ name: 'email', type: 'string', isPrimary: false, isNullable: false }

Database Schema IR:

{
  tables: [
    {
      name: 'User',
      fields: [
        { name: 'id', type: 'uuid', isPrimary: true, isNullable: false, isUnique: true },
        { name: 'name', type: 'string', isPrimary: false, isNullable: false },
        { name: 'email', type: 'string', isPrimary: false, isNullable: false }
      ]
    }
  ]
}

Generated schema.prisma:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id    String @id
  name  String
  email String
}

Files to Create/Modify

New Files:

  • packages/generators/src/prisma/generator.ts (NEW)
  • packages/generators/src/prisma/mapper.ts (NEW)
  • packages/generators/src/prisma/writer.ts (NEW)
  • packages/generators/src/prisma/index.ts (NEW)
  • tests/prisma.test.mjs (NEW)

Modified Files:

  • packages/generators/src/index.ts (add generatePrisma export)
  • packages/generators/package.json (if new dependencies needed)

Test Strategy

Create comprehensive test suite tests/prisma.test.mjs:

Unit Tests

  • Type mapper: each Forge type → Prisma type
  • Field modifiers: primary, unique, nullable
  • Multiple contracts in single schema
  • Unknown type detection and error handling

Integration Tests

  • Full pipeline: Contract → IR → schema.prisma
  • Generated schema syntax valid (parseable by Prisma)
  • Snapshot tests for golden outputs

Edge Cases

  • Optional string field → String?
  • Non-optional int field → Int
  • Multiple primary keys (error condition)
  • Empty contract (no fields)
  • Unicode in field names

Non-Goals

Explicitly out of scope (v0.3+):

  • Relationship definitions (@relation)
  • Foreign key constraints
  • Indexes beyond primary
  • Indexes beyond unique
  • Field-level constraints (e.g., @db.VarChar(255))
  • Migrations
  • Prisma client generation

Future Extensions

Phase 2 (v0.3):

  • Relationship support (OneToOne, OneToMany from contract references)
  • Custom indexes
  • Provider configuration

Phase 3 (v0.4):

  • Multi-database provider support (MySQL, SQLite)
  • Custom type attributes
  • Advanced constraints

Implementation Notes

  1. Critical: Only consume DatabaseSchema interface. Do NOT access SemanticModel or AST.
  2. Error handling: Throw with file location info
  3. Output: Write to generated/prisma/schema.prisma
  4. Formatting: Use standard Prisma formatting

Success Criteria

✅ Database Schema IR has first real consumer
✅ Validates IR architecture is sound
✅ Proves IR is reusable by multiple generators
✅ Unblocks next generation features (migrations, relationships)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions