Moving a Python backend to a TypeScript or polyglot stack usually means Prisma. SQLAlchemy's declarative models are thorough — a decade of production tables covered in Column(), Index(), Enum(), and relationship() calls — but they describe structure in a format Prisma cannot read. Translating by hand is hours of work and almost always drops something.
SchemaForge converts SQLAlchemy model 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 resolve an unrelated package. Use the source-install command above. Requires Python 3.10+.
Suppose you have a models.py with a User table:
from sqlalchemy import Column, Integer, String, Boolean, DateTime, Enum, Index
from sqlalchemy.orm import DeclarativeBase
import enum
class Role(enum.Enum):
admin = "admin"
viewer = "viewer"
editor = "editor"
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
__table_args__ = (
Index("ix_users_email", "email", unique=True),
)
id = Column(Integer, primary_key=True, autoincrement=True)
email = Column(String(255), nullable=False)
name = Column(String(120), nullable=True)
role = Column(Enum(Role), nullable=False, default=Role.viewer)
is_active = Column(Boolean, nullable=False, server_default="true")
created_at = Column(DateTime(timezone=True), server_default="now()")
schemaforge convert --from sqlalchemy --to prisma --input models.py --output schema.prisma
For a directory of model files:
schemaforge convert --from sqlalchemy --to prisma --input ./models/ --output schema.prisma
// schema.prisma — generated by SchemaForge
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum Role {
admin
viewer
editor
}
model User {
id Int @id @default(autoincrement())
email String @db.VarChar(255)
name String? @db.VarChar(120)
role Role @default(viewer)
is_active Boolean @default(true)
created_at DateTime @default(now()) @db.Timestamptz
@@index([email], map: "ix_users_email")
@@unique([email])
}
Column names, nullable flags, string lengths, enum values, index names, and server defaults all carry over. The @@unique constraint is inferred from the unique=True on the Index.
| SQLAlchemy feature | Prisma output | Fidelity |
|---|---|---|
Integer, BigInteger, SmallInteger | Int, BigInt | ✓ |
String(n), Text | String @db.VarChar(n), String | ✓ |
Boolean | Boolean | ✓ |
DateTime(timezone=True/False) | DateTime @db.Timestamptz / DateTime | ✓ |
Numeric(p, s) | Decimal @db.Decimal(p,s) | ✓ |
Enum (Python enum class) | Prisma enum block | ✓ |
primary_key=True, autoincrement=True | @id @default(autoincrement()) | ✓ |
nullable=False | non-optional field | ✓ |
server_default="now()", "gen_random_uuid()" | @default(now()), @default(uuid()) | ✓ |
Index(..., unique=True) | @@unique([...]) | ✓ |
ForeignKey, relationship() | Requires review after conversion manual step | ~ |
FOREIGN KEY/REFERENCES clauses and Prisma relation fields after conversion before applying the result.
Most projects split models across multiple files. SchemaForge merges them into one output schema:
schemaforge convert \
--from sqlalchemy \
--to prisma \
--input ./app/models/ \
--output prisma/schema.prisma
Models from all Python files in the directory are combined. Duplicate table names cause a validation error so you spot them early.
After conversion, diff the generated schema against your existing Prisma file (if you have one) to see exactly what changed:
schemaforge diff \
--format prisma \
--a prisma/schema.prisma \
--b prisma/schema.new.prisma
The diff output is field-level, not line-level — it surfaces added columns, changed types, and dropped constraints rather than whitespace noise.
SQLAlchemy custom types (TypeDecorator subclasses, JSONB, UUID, etc.) that SchemaForge does not recognise fall back to String by default. Override this with a YAML type-map:
# type-map.yaml
sqlalchemy_to_prisma:
UUID: String # or use @db.Uuid on Postgres
JSONB: Json
ARRAY: String # Prisma has no native Array; Json is a common substitute
schemaforge convert \
--from sqlalchemy \
--to prisma \
--input models.py \
--output schema.prisma \
--type-map type-map.yaml
The SchemaForge VS Code extension gives you a live preview panel: open a SQLAlchemy model file, trigger "Convert to Prisma", and see the schema rendered side-by-side before you write it to disk.
schemaforge convert on your model directory.@relation blocks for any ForeignKey columns (see the warning above).npx prisma validate to confirm the schema is syntactically correct.npx prisma migrate diff between the generated schema and your current database to catch gaps before the first migration.If Prisma is not your target, SchemaForge supports 11 schema formats and 100 conversion directions: SQL DDL, Prisma, Drizzle, TypeORM, Django, SQLAlchemy, Alembic migrations, JSON Schema, GraphQL SDL, EF Core C#, and Scala case classes. Alembic is generator-only (an output target, not a source).
SchemaForge is an MIT-licensed CLI tool built by Coding Dev Tools. Its shared representation preserves tables, columns, types, defaults, indexes, unique constraints, and enums. Review foreign-key constraints and ORM relationship fields after conversion.