opticore-orm-orchestrator
v1.0.0
Published
ORM Orchestrator for Prisma, TypeORM, Drizzle, MikroORM
Maintainers
Readme
OptiCore ORM Orchestrator
Multi-ORM CLI for TypeScript — A single interactive tool to generate models/entities for Prisma, TypeORM, Drizzle ORM, MikroORM and Sequelize — part of the OptiCoreJs Framework.
The orchestrator respects each ORM's native philosophy and generates exactly what each tool expects — no intermediate abstraction layer.
Table of Contents
- Supported ORMs
- Installation
- Quick Start
- CLI Commands
- Configuration File
- Field Types
- Relations
- ORM Adapters
- Generated Output Examples
- Next Steps After Generation
- Type Reference
Supported ORMs
| ORM | Generated file(s) | Source of truth |
|---|---|---|
| Prisma | prisma/schema.prisma | model {} block — no TypeScript file |
| TypeORM | src/entities/*.entity.ts | Class with @Entity, @Column decorators |
| Drizzle ORM | src/db/schema.ts | export const users = pgTable(...) |
| MikroORM | src/entities/*.entity.ts | Class with @Entity, @Property decorators |
| Sequelize | src/models/*.model.ts | Class extending Model<> with @Table, @Column |
Installation
# Local installation
npm install opticore-orm-orchestrator
# Global installation
npm install -g opticore-orm-orchestratorRun via:
npx orm-orchestrator <command>
# or
npm exec orm-orchestrator <command>Quick Start
# 1. Initialize the orchestrator and select your ORM
npx orm-orchestrator init
# 2. Generate a model interactively
npx orm-orchestrator make:model
# 3. List all defined models
npx orm-orchestrator listCLI Commands
init
Initializes the ORM orchestrator in the current project directory. Creates ormOrchestrator.json.
npx orm-orchestrator initInteractive steps:
- Select an ORM from the list
- (Prisma / Drizzle only) Select the database provider
- Optionally set the
DATABASE_URLconnection string - (TypeORM / MikroORM) Set the entities directory (default:
src/entities) - (Drizzle) Set the schema file path (default:
src/db/schema.ts) - (Sequelize) Set the models directory (default:
src/models)
Side effects:
- Writes
ormOrchestrator.jsonat the project root - Creates necessary directories (
prisma/,src/entities/,src/models/, etc.) - Creates
.envwith a defaultDATABASE_URLif not present - Creates
logs/app.logfor internal CLI logging
Generated ormOrchestrator.json examples:
// Prisma
{ "orm": "prisma", "prismaProvider": "postgresql" }
// TypeORM
{ "orm": "typeorm", "entitiesDir": "src/core/models" }
// Drizzle ORM
{ "orm": "drizzle", "prismaProvider": "postgresql", "drizzleSchema": "src/core/models/schema.ts" }
// MikroORM
{ "orm": "mikroorm", "entitiesDir": "src/core/models" }
// Sequelize
{ "orm": "sequelize", "modelsDir": "src/core/models" }make:model
Launches an interactive 5-step wizard to create a new model or entity.
npx orm-orchestrator make:model
# Aliases
npx orm-orchestrator generate
npx orm-orchestrator gWizard steps:
| Step | What it asks |
|------------------------|---|
| 1 — Model identity | Model name in PascalCase (e.g. UserProfile) |
| 2 — Primary key | Int autoincrement, String cuid(), String uuid(), or none |
| 3 — Custom fields | Enter a property name then its type. Press <return> with an empty name to stop |
| 4 — Relations | Target model, relation type table, field name, nullable, optional inverse property |
| 5 — Preview | Live code preview before writing to disk |
After confirmation, files are written and the relevant migration commands are shown.
Field type input
Types are entered as plain text. Typing ? at the type prompt prints the full type table and re-asks:
> Add another property? Enter the property name (or press <return> to stop adding fields):
title
> Field type (enter ? to see all types) [string]:
?
──────────── ──────────────────────────────
Type Description
──────────── ──────────────────────────────
string text / varchar
number integer or decimal
boolean true / false
Date date and time
bigint 64-bit integer
object JSON blob
Buffer binary data
──────────── ──────────────────────────────
> Field type (enter ? to see all types) [string]:
stringRelation wizard
Relations are configured through a Symfony-style interactive flow:
> What class should this entity be related to?
Category
What type of relationship is this?
────────────── ──────────────────────────────────────────────────────────────
Type Description
────────────── ──────────────────────────────────────────────────────────────
ManyToOne Each Article relates to (has) one Category.
Each Category can relate to (can have) many Article objects
OneToMany Each Article can relate to (can have) many Category objects.
Each Category relates to (has) one Article
ManyToMany Each Article can relate to (can have) many Category objects.
Each Category can also relate to (can also have) many Article objects
OneToOne Each Article relates to (has) exactly one Category.
Each Category also relates to (has) exactly one Article.
────────────── ──────────────────────────────────────────────────────────────
> Relation type? [ManyToOne, OneToMany, ManyToMany, OneToOne]:
ManyToOne
> Field name in Article: [category]
> Is the Article.category property allowed to be null (nullable)? (yes/no)
yes
> Do you want to add a new property to Category so that you can access/update Article objects from it? – e.g. category.getArticles() (yes/no)
yes
A new property will also be added to the Category class so that you can access the related Article objects from it.
> New field name inside Category: [articles]list
Lists all models/entities already defined in the current project.
npx orm-orchestrator list
# Alias
npx orm-orchestrator lsOutput:
✦ ORM Orchestrator — Defined models
ORM: Prisma | Writes the model{} block directly in prisma/schema.prisma
3 model(s):
User prisma/schema.prisma
Post prisma/schema.prisma
Comment prisma/schema.prismaConfiguration File
ormOrchestrator.json is written by init and read by every other command.
interface OrmOrchestratorConfig {
orm: 'prisma' | 'typeorm' | 'drizzle' | 'mikroorm' | 'sequelize';
prismaProvider?: 'postgresql' | 'mysql' | 'sqlite' | 'sqlserver' | 'mongodb';
databaseUrl?: string;
entitiesDir?: string;
modelsDir?: string;
drizzleSchema?: string;
}Good to know:
Drizzle : schema file path. Default: "src/core/models/schema.ts"
Sequelize : models directory. Default: "src/core/models"
TypeORM : MikroORM — entities directory. Default: "src/core/models"
Optional : stored connection string (otherwise read from DATABASE_URL env var)
Prisma : Drizzle — database driver
The active ORMField Types
| Wizard label | TypeScript | Prisma | TypeORM | Drizzle (pg) | Sequelize |
|---|---|---|---|---|---|
| string | string | String | varchar | text | STRING |
| number | number | Int | int | integer | INTEGER |
| boolean | boolean | Boolean | boolean | boolean | BOOLEAN |
| Date | Date | DateTime | timestamp | timestamp | DATE |
| bigint | bigint | BigInt | bigint | bigint | BIGINT |
| object | object | Json | json | jsonb | JSON |
| Buffer | Buffer | Bytes | bytea | bytea | BLOB |
Type names are entered as free text. Enter ? at the type prompt to display the full table above and re-prompt.
Field options available during the wizard:
| Option | Description |
|---|---|
| Default value | autoincrement(), cuid(), uuid(), now(), true/false, or a literal string/number |
| Unique | Adds a UNIQUE constraint |
| Nullable | Makes the field optional (? in TypeScript, NULL in SQL) |
Relations
All four standard association types are supported. The wizard always asks whether to declare an inverse property on the target model (e.g. adding articles inside Category so you can navigate back from a Category to its Article objects).
OneToMany — current model has many of target
User ─── (has many) ──► Post| ORM | Generated output |
|---|---|
| Prisma | posts Post[] on User; inverse ManyToOne injected automatically into Post |
| TypeORM | @OneToMany(() => Post, post => post.user) |
| MikroORM | @OneToMany(() => Post, ...) with Collection<Post> |
| Sequelize | @HasMany(() => Post) |
ManyToOne — current model belongs to target
Post ─── (belongs to) ──► UserGenerates a foreign-key column (e.g. userId) alongside the relation field.
| ORM | Generated output |
|---|---|
| Prisma | userId Int? + @relation(fields: [userId], references: [id]) |
| TypeORM | @ManyToOne + @JoinColumn({ name: 'userId' }) |
| MikroORM | @ManyToOne(() => User, { nullable: true }) |
| Sequelize | Explicit @ForeignKey column + @BelongsTo |
OneToOne — current model has exactly one of target
Generates a @unique foreign-key column on the owning side.
| ORM | Generated output |
|---|---|
| Prisma | @unique on the FK field |
| TypeORM | @OneToOne + @JoinColumn |
| MikroORM | @OneToOne(() => Target) |
| Sequelize | @BelongsTo (owning side) or @HasOne (inverse) |
ManyToMany — shares many of target via pivot table
| ORM | Generated output |
|---|---|
| Prisma | Target[] field (implicit pivot table managed by Prisma) |
| TypeORM | @ManyToMany + @JoinTable({ name: '...' }) |
| MikroORM | @ManyToMany with Collection<Target> |
| Sequelize | @BelongsToMany + a dedicated join-table model file is generated automatically |
ORM Adapters
Prisma
Strategy: Writes model {} blocks directly into prisma/schema.prisma. No TypeScript entity file.
- Creates
prisma/schema.prismawithgenerator clientanddatasource dbblocks if absent - Updates existing models in place using regex matching
- Automatically injects inverse relation fields into already-defined models
TypeORM
Strategy: Generates src/entities/<name>.entity.ts — the TypeScript class is the entity.
- Only the necessary decorators are imported (tree-shaken import list)
- Related entity imports are added automatically
- Supports
@PrimaryGeneratedColumn(),@PrimaryGeneratedColumn('uuid'),@CreateDateColumn(),@UpdateDateColumn()
Drizzle ORM
Strategy: Generates/updates a single src/db/schema.ts — the TypeScript file is the schema.
- Driver-aware: uses
pgTable/mysqlTable/sqliteTablebased onprismaProvider - SQL function defaults (e.g.
now()) use thesqltemplate tag fromdrizzle-orm - Relations declared with the
relations()helper in the same file - Merges missing
sqlandrelationsimports automatically on each write
MikroORM
Strategy: Generates src/entities/<name>.entity.ts — the TypeScript class is the entity.
OneToManyandManyToManyuseCollection<T>initialized inline- Decorator options are built dynamically (only non-default options are included)
Sequelize
Strategy: Generates src/models/<name>.model.ts — the class extends Model<> from sequelize-typescript.
Extra files generated automatically:
| Situation | Extra file |
|---|---|
| ManyToMany relation | Dedicated join-table model (e.g. user-tag.model.ts) |
| Any model | src/models/index.ts barrel is created or updated |
Generated Output Examples
Prisma
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String @unique
name String?
posts Post[]
@@map("users")
}TypeORM
// src/entities/post.entity.ts
import {
Entity, Column, PrimaryGeneratedColumn,
CreateDateColumn, UpdateDateColumn,
ManyToOne, JoinColumn
} from 'typeorm';
import { User } from './user.entity';
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id!: number;
@CreateDateColumn()
createdAt!: Date;
@UpdateDateColumn()
updatedAt!: Date;
@Column('varchar', { unique: true })
title!: string;
@ManyToOne(() => User, (user) => user.posts, { nullable: true })
@JoinColumn({ name: 'userId' })
user?: User;
}Drizzle ORM (PostgreSQL)
// src/db/schema.ts
import { pgTable, relations } from 'drizzle-orm/pg-core';
import { sql } from 'drizzle-orm';
export const posts = pgTable('posts', {
id: integer('id').primaryKey().autoIncrement(),
createdAt: timestamp('createdAt').default(sql`now()`).notNull(),
updatedAt: timestamp('updatedAt').$onUpdate(() => new Date()),
title: text('title').notNull(),
userId: integer('userId'),
});
export const postsRelations = relations(posts, ({ one }) => ({
user: one(users, { fields: [posts.userId], references: [users.id] }),
}));MikroORM
// src/entities/comment.entity.ts
import {
Entity, Property, PrimaryKey, ManyToOne
} from '@mikro-orm/core';
import { Post } from './post.entity';
@Entity()
export class Comment {
@PrimaryKey()
id!: number;
@Property({ default: 'now()' })
createdAt!: Date;
@Property({ onUpdate: () => new Date() })
updatedAt!: Date;
@Property()
body!: string;
@ManyToOne(() => Post, { nullable: true })
post?: Post;
}Sequelize
// src/models/user.model.ts
import {
Table, Column, Model, DataType,
PrimaryKey, AutoIncrement,
CreatedAt, UpdatedAt, Unique, HasMany
} from 'sequelize-typescript';
import { Post } from './post.model';
@Table({ timestamps: true, underscored: true })
export class User extends Model<User> {
@PrimaryKey
@AutoIncrement
@Column(DataType.INTEGER)
id!: number;
@CreatedAt
createdAt!: Date;
@UpdatedAt
updatedAt!: Date;
@Unique
@Column({ type: DataType.STRING, allowNull: false })
email!: string;
@HasMany(() => Post, { foreignKey: 'userId', onDelete: 'SET NULL' })
posts?: Post[];
}// src/models/post-tag.model.ts ← auto-generated for ManyToMany
import { Table, Column, Model, ForeignKey } from 'sequelize-typescript';
import { Post } from './post.model';
import { Tag } from './tag.model';
@Table({ tableName: 'post_tag', timestamps: false })
export class PostTag extends Model<PostTag> {
@ForeignKey(() => Post)
@Column
postId!: number;
@ForeignKey(() => Tag)
@Column
tagId!: number;
}// src/models/index.ts ← barrel auto-maintained
export { User } from './user.model';
export { Post } from './post.model';
export { Tag } from './tag.model';
export { PostTag } from './post-tag.model';Next Steps After Generation
| ORM | Commands |
|---|---|
| Prisma | npx prisma migrate dev · npx prisma generate |
| TypeORM | npm run migration:generate · npm run migration:run |
| Drizzle ORM | npx drizzle-kit generate · npx drizzle-kit migrate |
| MikroORM | npm run migration:generate · npm run migration:run |
| Sequelize | npx sequelize-cli db:migrate |
Type Reference
OrmType
type OrmType = 'prisma' | 'typeorm' | 'drizzle' | 'mikroorm' | 'sequelize';ScalarType
type ScalarType = 'string' | 'number' | 'boolean' | 'Date' | 'bigint' | 'object' | 'Buffer';RelationType
type RelationType = 'OneToOne' | 'OneToMany' | 'ManyToOne' | 'ManyToMany';FieldDefinition
interface FieldDefinition {
name: string;
type: ScalarType;
default?: string | number | boolean;
primary?: boolean;
unique?: boolean;
nullable?: boolean;
onUpdate?: boolean;
columnName?: string;
length?: number;
}Good to know
default? : autoincrement()', 'now()', 'cuid()', true, 0, …
onUpdate? : @updatedAt / @UpdateDateColumn / $onUpdate hook
columnName?: Custom SQL column name (overrides field name)
length?: Column length — used by VARCHAR / STRINGRelationDefinition
interface RelationDefinition {
type: RelationType;
target: string;
field: string;
foreignKey?: string;
references?: string;
nullable?: boolean;
joinTable?: string;
inverseField?: string;
}Good to know
target: Target model name (PascalCase)
field: Property name on the owning model
foreignKey?: FK column name — default: `${field}Id`
references?: Referenced column — default: 'id'
nullable?: Whether the relation field is nullable
joinTable?: Explicit pivot table name (ManyToMany)
inverseField?: Property name added to the target model for the inverse side
(e.g. "articles" inside Category pointing back to Article)ModelDefinition
interface ModelDefinition {
name: string;
fields: FieldDefinition[];
relations?: RelationDefinition[];
tableName?: string;
}Good to know
tableName?: Custom SQL table name (@@map / tableName option)
installed: Packages already in node_modules
missing: Production packages to install
missingDev: Dev packages to installOrmOrchestratorConfig
interface OrmOrchestratorConfig {
orm: OrmType;
prismaProvider?: 'postgresql' | 'mysql' | 'sqlite' | 'sqlserver' | 'mongodb';
databaseUrl?: string;
entitiesDir?: string;
modelsDir?: string;
drizzleSchema?: string;
}CheckResult
interface CheckResult {
installed: string[];
missing: string[];
missingDev: string[];
}Good to know
installed: Packages already in node_modules
missing: Production packages to install
missingDev: Dev packages to installContributors
This package is led by Guy-serge Kouacou.
Contributing
This project welcomes contributions from the community. Contributions are accepted using GitHub pull requests. If you're not familiar with making GitHub pull requests, please refer to the GitHub documentation "Creating a pull request."
License
MIT — part of the OptiCoreJs Framework.
