Moving between TypeORM and Prisma means translating model definitions into a different schema language and workflow.
The migration itself is the problem. A medium-size service with 20–40 TypeORM entity classes means hundreds of fields, custom column types, composite indexes, and enum definitions to translate by hand. One missed @Column({ unique: true }) and you drop a constraint in production.
SchemaForge converts TypeORM entity files to a Prisma schema as a reviewable starting point. Its shared representation preserves tables, columns, types, defaults, indexes, unique constraints, and enums; relationship details still need review before you apply generated output.
# Install from source (PyPI publishing pending)
pip install git+https://github.com/Coding-Dev-Tools/schemaforge.git
pip install schemaforge will fail or install an unrelated package. Use the source-install command above. Requires Python 3.10+.
Start with a representative entity. Most teams have something like this:
// src/entities/user.entity.ts
import {
Entity, PrimaryGeneratedColumn, Column,
CreateDateColumn, UpdateDateColumn, Index, Unique
} from 'typeorm';
export enum UserRole { ADMIN = 'admin', MEMBER = 'member', VIEWER = 'viewer' }
@Entity('users')
@Index(['email'])
@Unique(['email'])
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ length: 100 })
name: string;
@Column({ unique: true, length: 255 })
email: string;
@Column({ type: 'enum', enum: UserRole, default: UserRole.MEMBER })
role: UserRole;
@Column({ nullable: true })
avatarUrl: string | null;
@Column({ default: true })
isActive: boolean;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}
schemaforge convert --from typeorm --to prisma \
--input src/entities/user.entity.ts \
--output prisma/schema.prisma
schemaforge convert --from typeorm --to prisma \
--input src/entities/ \
--output prisma/schema.prisma
SchemaForge reads all .ts files in the directory, merges them into a single Prisma schema, and resolves shared enums across entity files.
// prisma/schema.prisma (generated)
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum UserRole {
admin
member
viewer
}
model User {
id String @id @default(uuid())
name String @db.VarChar(100)
email String @unique @db.VarChar(255)
role UserRole @default(member)
avatarUrl String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("users")
@@index([email])
}
The @map directive preserves the original table name from @Entity('users'), @db.VarChar preserves column-length constraints, and the unique index from both @Unique(['email']) and @Column({ unique: true }) collapses correctly to a single @unique.
If your entities are spread across subdirectories, run a conversion for each service:
# Convert each service's entities separately
for service in users orders billing; do
schemaforge convert --from typeorm --to prisma \
--input "services/$service/entities/" \
--output "services/$service/prisma/schema.prisma"
done
| TypeORM feature | In SchemaForge output |
|---|---|
Columns (@Column) + types + lengths | ✓ Preserved |
Nullability (nullable: true/false) | ✓ Preserved |
Defaults (literals, now(), uuid()) | ✓ Preserved |
Primary keys (@PrimaryGeneratedColumn) | ✓ Preserved |
Unique constraints (@Unique, unique: true) | ✓ Preserved |
Indexes (@Index) | ✓ Preserved |
Enums (type: 'enum') | ✓ Preserved |
Table name from @Entity('name') | ✓ as @@map |
| Create/Update timestamps | ✓ as @default(now()) / @updatedAt |
Relations (@ManyToOne, @OneToMany, etc.) | ⚠ Dropped — see note below |
Foreign keys / @JoinColumn | ⚠ Dropped — see note below |
| Embedded entities | ⚠ Review after conversion |
@ManyToOne, @OneToMany, @ManyToMany) are dropped during conversion. SchemaForge's shared representation preserves tables, columns, types, defaults, indexes, unique constraints, and enums; review and add the relationship details before applying generated output.
The mechanical structural work is automated; relational semantics receive a deliberate review before the generated schema is applied.
Once you've added relations manually, use SchemaForge's diff command to compare the original TypeORM-derived schema against an updated version:
# Compare two Prisma schema files
schemaforge diff prisma/schema-v1.prisma prisma/schema-v2.prisma
The output shows added, removed, and modified tables, columns, indexes, and constraints — useful for catching regressions during the migration review.
TypeORM lets you use raw database types like jsonb, tsvector, or custom Postgres enums. If SchemaForge's defaults don't match what you want in Prisma, supply a mapping file:
# type-map.yaml
jsonb: Json
tsvector: String
citext: String
schemaforge convert --from typeorm --to prisma \
--input src/entities/ \
--output prisma/schema.prisma \
--type-map type-map.yaml
SchemaForge ships a VS Code extension with live preview: open a TypeORM entity file and see the Prisma output update as you type. Install it from the VS Code Marketplace.
If you use Claude Code, Cursor, or another MCP-compatible editor, SchemaForge can run as an MCP server so an assistant can call its conversion, diff, and check tools directly:
pip install "git+https://github.com/Coding-Dev-Tools/schemaforge.git[mcp]"
schemaforge mcp
The MCP server exposes convert, diff, and check as tools. Generated output remains a starting point: review the missing relationship details before applying it.
SchemaForge reduces the mechanical column-copying work of a TypeORM → Prisma migration. It supports 11 schema formats and 100 conversion directions; Alembic is generator-only. Use generated output as a starting point, then spend review time on relationship fields, query rewrites, and transaction patterns.
Get SchemaForge on GitHub →SchemaForge is an open-source CLI by Coding Dev Tools. See also: SchemaForge overview · GitHub