ORM Party

Setup

Select ORMs to compare

Pick one or more ORMs from the bar above

Connect to Database

SQL

-- via connection string
psql "postgresql://user:password@localhost:5432/mydb"

-- via parameters
psql -h localhost -p 5432 -U user -d mydb

Django ORM

import os

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": os.environ.get("DB_NAME"),
        "USER": os.environ.get("DB_USER"),
        "PASSWORD": os.environ.get("DB_PASSWORD"),
        "HOST": os.environ.get("DB_HOST", "localhost"),
        "PORT": os.environ.get("DB_PORT", "5432"),
    }
}

SQLAlchemy

import os
from sqlalchemy import create_engine
from sqlalchemy.orm import Session

engine = create_engine(os.environ["DATABASE_URL"])

SQLModel

import os
from sqlmodel import Session, create_engine

engine = create_engine(os.environ["DATABASE_URL"])

Tortoise ORM

from tortoise import Tortoise

await Tortoise.init(
    db_url="postgres://user:pass@localhost:5432/mydb",
    modules={"models": ["app.models"]},
)

Peewee

import os
from peewee import PostgresqlDatabase

db = PostgresqlDatabase(
    os.environ.get("DB_NAME"),
    user=os.environ.get("DB_USER"),
    password=os.environ.get("DB_PASSWORD"),
    host=os.environ.get("DB_HOST", "localhost"),
    port=int(os.environ.get("DB_PORT", 5432)),
)

Prisma

import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

Drizzle

import { drizzle } from "drizzle-orm/node-postgres";

const db = drizzle(process.env.DATABASE_URL!);

Kysely

import { Kysely, PostgresDialect } from 'kysely'
import { Pool } from 'pg'

interface DB {
  users: {
    id: number
    name: string
    email: string
    created_at: Date
  }
}

const db = new Kysely<DB>({
  dialect: new PostgresDialect({
    pool: new Pool({
      connectionString: process.env.DATABASE_URL,
    }),
  }),
})

TypeORM

import { DataSource } from "typeorm";
import { User } from "./entity";

const AppDataSource = new DataSource({
  type: "postgres",
  url: process.env.DATABASE_URL,
  entities: [User],
  synchronize: true,
});

await AppDataSource.initialize();

MikroORM

import { MikroORM } from "@mikro-orm/postgresql";
import { User } from "./entity";

const orm = await MikroORM.init({
  dbName: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  host: process.env.DB_HOST ?? "localhost",
  port: Number(process.env.DB_PORT ?? 5432),
  entities: [User],
});

Sequelize

import { Sequelize } from "sequelize";

const sequelize = new Sequelize(process.env.DATABASE_URL!, {
  dialect: "postgres",
  logging: false,
});

await sequelize.authenticate();

Define a Model

SQL

CREATE TABLE users (
    id         SERIAL PRIMARY KEY,
    name       VARCHAR(255) NOT NULL,
    email      VARCHAR(255) NOT NULL UNIQUE,
    created_at TIMESTAMP NOT NULL DEFAULT now()
);

Django ORM

from django.db import models


class User(models.Model):
    name = models.CharField(max_length=255)
    email = models.EmailField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)

SQLAlchemy

from datetime import datetime
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import String, func


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(255))
    email: Mapped[str] = mapped_column(String(255), unique=True)
    created_at: Mapped[datetime] = mapped_column(server_default=func.now())

SQLModel

from datetime import datetime
from sqlmodel import SQLModel, Field


class User(SQLModel, table=True):
    __tablename__ = "users"

    id: int | None = Field(default=None, primary_key=True)
    name: str = Field(max_length=255)
    email: str = Field(unique=True, max_length=255)
    created_at: datetime = Field(default_factory=datetime.utcnow)

Tortoise ORM

from tortoise.models import Model
from tortoise import fields

class User(Model):
    name = fields.CharField(max_length=255)
    email = fields.CharField(max_length=255, unique=True)
    created_at = fields.DatetimeField(auto_now_add=True)

Peewee

from datetime import datetime
from peewee import CharField, DateTimeField, Model


class User(Model):
    name = CharField(max_length=255)
    email = CharField(max_length=255, unique=True)
    created_at = DateTimeField(default=datetime.utcnow)

    class Meta:
        table_name = "users"

Prisma

model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  createdAt DateTime @default(now())
}

Drizzle

import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: serial("id").primaryKey(),
  name: text("name").notNull(),
  email: text("email").notNull().unique(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
});

Kysely

import { Generated } from 'kysely'

export interface UserTable {
  id: Generated<number>
  name: string
  email: string
  created_at: Date
}

export interface DB {
  users: UserTable
}

TypeORM

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from "typeorm";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column({ unique: true })
  email: string;

  @CreateDateColumn()
  createdAt: Date;
}

MikroORM

import { Entity, PrimaryKey, Property } from "@mikro-orm/core";

@Entity({ tableName: "users" })
export class User {
  @PrimaryKey()
  id!: number;

  @Property({ length: 255 })
  name!: string;

  @Property({ length: 255, unique: true })
  email!: string;

  @Property({ fieldName: "created_at", onCreate: () => new Date() })
  createdAt = new Date();
}

Sequelize

import {
  CreationOptional,
  DataTypes,
  InferAttributes,
  InferCreationAttributes,
  Model,
  Sequelize,
} from "sequelize";

const sequelize = new Sequelize(process.env.DATABASE_URL!, {
  dialect: "postgres",
});

export class User extends Model<
  InferAttributes<User>,
  InferCreationAttributes<User>
> {
  declare id: CreationOptional<number>;
  declare name: string;
  declare email: string;
  declare createdAt: CreationOptional<Date>;
}

User.init(
  {
    id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true,
    },
    name: {
      type: DataTypes.STRING(255),
      allowNull: false,
    },
    email: {
      type: DataTypes.STRING(255),
      allowNull: false,
      unique: true,
    },
    createdAt: {
      type: DataTypes.DATE,
      allowNull: false,
      defaultValue: DataTypes.NOW,
      field: "created_at",
    },
  },
  {
    sequelize,
    tableName: "users",
    timestamps: false,
  },
);

Define Field Types

SQL

CREATE TABLE products (
    id           SERIAL PRIMARY KEY,
    name         VARCHAR(255) NOT NULL,
    description  TEXT NOT NULL,
    price        DOUBLE PRECISION NOT NULL,
    quantity     INTEGER NOT NULL,
    in_stock     BOOLEAN NOT NULL,
    release_date TIMESTAMP NOT NULL,
    tags         JSONB NOT NULL
);

Django ORM

from django.db import models


class Product(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    price = models.FloatField()
    quantity = models.IntegerField()
    in_stock = models.BooleanField()
    release_date = models.DateTimeField()
    tags = models.JSONField()

SQLAlchemy

from datetime import datetime
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import String, Text, JSON


class Base(DeclarativeBase):
    pass


class Product(Base):
    __tablename__ = "products"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(255))
    description: Mapped[str] = mapped_column(Text)
    price: Mapped[float] = mapped_column()
    quantity: Mapped[int] = mapped_column()
    in_stock: Mapped[bool] = mapped_column()
    release_date: Mapped[datetime] = mapped_column()
    tags: Mapped[dict] = mapped_column(JSON)

SQLModel

from datetime import datetime
from sqlmodel import SQLModel, Field
from sqlalchemy import Column, JSON, Text


class Product(SQLModel, table=True):
    __tablename__ = "products"

    id: int | None = Field(default=None, primary_key=True)
    name: str = Field(max_length=255)
    description: str = Field(sa_column=Column(Text))
    price: float
    quantity: int
    in_stock: bool
    release_date: datetime
    tags: dict = Field(default_factory=dict, sa_column=Column(JSON))

Tortoise ORM

from tortoise.models import Model
from tortoise import fields

class Product(Model):
    name = fields.CharField(max_length=255)
    description = fields.TextField()
    price = fields.FloatField()
    quantity = fields.IntField()
    in_stock = fields.BooleanField()
    release_date = fields.DatetimeField()
    tags = fields.JSONField()

Peewee

from datetime import datetime
from peewee import (
    BooleanField,
    CharField,
    DateTimeField,
    FloatField,
    IntegerField,
    Model,
    TextField,
)
from playhouse.postgres_ext import BinaryJSONField


class Product(Model):
    name = CharField(max_length=255)
    description = TextField()
    price = FloatField()
    quantity = IntegerField()
    in_stock = BooleanField()
    release_date = DateTimeField()
    tags = BinaryJSONField(default=dict)

    class Meta:
        table_name = "products"

Prisma

model Product {
  id          Int      @id @default(autoincrement())
  name        String
  description String   @db.Text
  price       Float
  quantity    Int
  inStock     Boolean
  releaseDate DateTime
  tags        Json
}

Drizzle

import {
  pgTable, serial, text, integer,
  doublePrecision, boolean, timestamp, jsonb,
} from "drizzle-orm/pg-core";

export const products = pgTable("products", {
  id: serial("id").primaryKey(),
  name: text("name").notNull(),
  description: text("description").notNull(),
  price: doublePrecision("price").notNull(),
  quantity: integer("quantity").notNull(),
  inStock: boolean("in_stock").notNull(),
  releaseDate: timestamp("release_date").notNull(),
  tags: jsonb("tags"),
});

Kysely

import { Generated } from 'kysely'

export interface ProductTable {
  id: Generated<number>
  name: string
  description: string
  price: number
  quantity: number
  in_stock: boolean
  release_date: Date
  tags: Record<string, unknown>
}

export interface DB {
  products: ProductTable
}

TypeORM

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()
export class Product {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column("text")
  description: string;

  @Column("float")
  price: number;

  @Column("int")
  quantity: number;

  @Column()
  inStock: boolean;

  @Column("timestamp")
  releaseDate: Date;

  @Column("jsonb")
  tags: any;
}

MikroORM

import { Entity, PrimaryKey, Property } from "@mikro-orm/core";

@Entity({ tableName: "products" })
export class Product {
  @PrimaryKey()
  id!: number;

  @Property({ length: 255 })
  name!: string;

  @Property({ type: "text" })
  description!: string;

  @Property({ type: "float" })
  price!: number;

  @Property({ type: "int" })
  quantity!: number;

  @Property({ fieldName: "in_stock" })
  inStock!: boolean;

  @Property({ fieldName: "release_date" })
  releaseDate!: Date;

  @Property({ type: "json" })
  tags!: Record<string, unknown>;
}

Sequelize

import {
  CreationOptional,
  DataTypes,
  InferAttributes,
  InferCreationAttributes,
  Model,
  Sequelize,
} from "sequelize";

const sequelize = new Sequelize(process.env.DATABASE_URL!, {
  dialect: "postgres",
});

export class Product extends Model<
  InferAttributes<Product>,
  InferCreationAttributes<Product>
> {
  declare id: CreationOptional<number>;
  declare name: string;
  declare description: string;
  declare price: number;
  declare quantity: number;
  declare inStock: boolean;
  declare releaseDate: Date;
  declare tags: Record<string, unknown>;
}

Product.init(
  {
    id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true,
    },
    name: { type: DataTypes.STRING(255), allowNull: false },
    description: { type: DataTypes.TEXT, allowNull: false },
    price: { type: DataTypes.FLOAT, allowNull: false },
    quantity: { type: DataTypes.INTEGER, allowNull: false },
    inStock: { type: DataTypes.BOOLEAN, allowNull: false, field: "in_stock" },
    releaseDate: { type: DataTypes.DATE, allowNull: false, field: "release_date" },
    tags: { type: DataTypes.JSONB, allowNull: false, defaultValue: {} },
  },
  {
    sequelize,
    tableName: "products",
    timestamps: false,
  },
);

Nullable & Default Values

SQL

CREATE TABLE posts (
    id           SERIAL PRIMARY KEY,
    title        VARCHAR(255) NOT NULL,
    content      TEXT,
    views        INTEGER NOT NULL DEFAULT 0,
    is_published BOOLEAN NOT NULL DEFAULT false,
    published_at TIMESTAMP,
    created_at   TIMESTAMP NOT NULL DEFAULT now()
);

Django ORM

from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField(null=True, blank=True)
    views = models.IntegerField(default=0)
    is_published = models.BooleanField(default=False)
    published_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

SQLAlchemy

from datetime import datetime
from typing import Optional
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import String, Text, func


class Base(DeclarativeBase):
    pass


class Post(Base):
    __tablename__ = "posts"

    id: Mapped[int] = mapped_column(primary_key=True)
    title: Mapped[str] = mapped_column(String(255))
    content: Mapped[Optional[str]] = mapped_column(Text)
    views: Mapped[int] = mapped_column(default=0)
    is_published: Mapped[bool] = mapped_column(default=False)
    published_at: Mapped[Optional[datetime]] = mapped_column()
    created_at: Mapped[datetime] = mapped_column(server_default=func.now())

SQLModel

from datetime import datetime
from sqlmodel import SQLModel, Field
from sqlalchemy import Column, Text


class Post(SQLModel, table=True):
    __tablename__ = "posts"

    id: int | None = Field(default=None, primary_key=True)
    title: str = Field(max_length=255)
    content: str | None = Field(default=None, sa_column=Column(Text, nullable=True))
    views: int = 0
    is_published: bool = False
    published_at: datetime | None = None
    created_at: datetime = Field(default_factory=datetime.utcnow)

Tortoise ORM

from tortoise.models import Model
from tortoise import fields

class Post(Model):
    title = fields.CharField(max_length=255)
    content = fields.TextField(null=True)
    views = fields.IntField(default=0)
    is_published = fields.BooleanField(default=False)
    published_at = fields.DatetimeField(null=True)
    created_at = fields.DatetimeField(auto_now_add=True)

Peewee

from datetime import datetime
from peewee import BooleanField, CharField, DateTimeField, IntegerField, Model, TextField


class Post(Model):
    title = CharField(max_length=255)
    content = TextField(null=True)
    views = IntegerField(default=0)
    is_published = BooleanField(default=False)
    published_at = DateTimeField(null=True)
    created_at = DateTimeField(default=datetime.utcnow)

    class Meta:
        table_name = "posts"

Prisma

model Post {
  id          Int       @id @default(autoincrement())
  title       String
  content     String?
  views       Int       @default(0)
  isPublished Boolean   @default(false)
  publishedAt DateTime?
  createdAt   DateTime  @default(now())
}

Drizzle

import { pgTable, serial, text, integer, boolean, timestamp } from "drizzle-orm/pg-core";

export const posts = pgTable("posts", {
  id: serial("id").primaryKey(),
  title: text("title").notNull(),
  content: text("content"),
  views: integer("views").default(0).notNull(),
  isPublished: boolean("is_published").default(false).notNull(),
  publishedAt: timestamp("published_at"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
});

Kysely

import { Generated } from 'kysely'

export interface PostTable {
  id: Generated<number>
  title: string
  content: string | null
  views: number
  is_published: boolean
  published_at: Date | null
  created_at: Date
}

export interface DB {
  posts: PostTable
}

TypeORM

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from "typeorm";

@Entity()
export class Post {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column({ nullable: true })
  content: string;

  @Column({ default: 0 })
  views: number;

  @Column({ default: false })
  isPublished: boolean;

  @Column({ nullable: true })
  publishedAt: Date;

  @CreateDateColumn()
  createdAt: Date;
}

MikroORM

import { Entity, PrimaryKey, Property } from "@mikro-orm/core";

@Entity({ tableName: "posts" })
export class Post {
  @PrimaryKey()
  id!: number;

  @Property({ length: 255 })
  title!: string;

  @Property({ type: "text", nullable: true })
  content?: string;

  @Property({ default: 0 })
  views = 0;

  @Property({ fieldName: "is_published", default: false })
  isPublished = false;

  @Property({ fieldName: "published_at", nullable: true })
  publishedAt?: Date;

  @Property({ fieldName: "created_at", onCreate: () => new Date() })
  createdAt = new Date();
}

Sequelize

import {
  CreationOptional,
  DataTypes,
  InferAttributes,
  InferCreationAttributes,
  Model,
  Sequelize,
} from "sequelize";

const sequelize = new Sequelize(process.env.DATABASE_URL!, {
  dialect: "postgres",
});

export class Post extends Model<
  InferAttributes<Post>,
  InferCreationAttributes<Post>
> {
  declare id: CreationOptional<number>;
  declare title: string;
  declare content: string | null;
  declare views: CreationOptional<number>;
  declare isPublished: CreationOptional<boolean>;
  declare publishedAt: Date | null;
  declare createdAt: CreationOptional<Date>;
}

Post.init(
  {
    id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true,
    },
    title: { type: DataTypes.STRING(255), allowNull: false },
    content: { type: DataTypes.TEXT, allowNull: true },
    views: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 },
    isPublished: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
      defaultValue: false,
      field: "is_published",
    },
    publishedAt: { type: DataTypes.DATE, allowNull: true, field: "published_at" },
    createdAt: {
      type: DataTypes.DATE,
      allowNull: false,
      defaultValue: DataTypes.NOW,
      field: "created_at",
    },
  },
  {
    sequelize,
    tableName: "posts",
    timestamps: false,
  },
);

Create / Sync Schema

SQL

-- No sync tool — you run CREATE TABLE directly
-- or use a migration tool like Flyway / Liquibase

Django ORM

python manage.py makemigrations
python manage.py migrate

SQLAlchemy

import os
from sqlalchemy import create_engine
from models import Base

engine = create_engine(os.environ["DATABASE_URL"])
Base.metadata.create_all(engine)

SQLModel

import os
from sqlmodel import SQLModel, create_engine

engine = create_engine(os.environ["DATABASE_URL"])
SQLModel.metadata.create_all(engine)

Tortoise ORM

from tortoise import Tortoise

await Tortoise.init(
    db_url="postgres://user:pass@localhost:5432/mydb",
    modules={"models": ["app.models"]},
)
await Tortoise.generate_schemas()

Peewee

# Assumes db and models are imported

db.connect()
db.create_tables([User, Post, Order])
db.close()

Prisma

npx prisma db push

Drizzle

npx drizzle-kit push

Kysely

# Kysely has no schema push command.
# Apply schema via migrations.
$ bun run migrate:up

TypeORM

npx typeorm schema:sync

MikroORM

$ npx mikro-orm schema:update --run

Sequelize

$ npx sequelize-cli db:migrate