ORM Party

Raw Queries

Select ORMs to compare

Pick one or more ORMs from the bar above

Execute Raw SQL

SQL

SELECT id, name, email
FROM users
WHERE created_at > '2024-01-01';

Django ORM

from django.db import connection

with connection.cursor() as cursor:
    cursor.execute(
        "SELECT id, name, email FROM users "
        "WHERE created_at > '2024-01-01'"
    )
    rows = cursor.fetchall()

SQLAlchemy

from sqlalchemy import text

with engine.connect() as conn:
    result = conn.execute(
        text(
            "SELECT id, name, email FROM users "
            "WHERE created_at > '2024-01-01'"
        )
    )
    rows = result.fetchall()

SQLModel

from sqlalchemy import text

with engine.connect() as conn:
    result = conn.execute(
        text(
            "SELECT id, name, email FROM users "
            "WHERE created_at > '2024-01-01'"
        )
    )
    rows = result.fetchall()

Tortoise ORM

from tortoise import connections

conn = connections.get("default")
rows = await conn.execute_query_dict(
    "SELECT id, name, email FROM users "
    "WHERE created_at > '2024-01-01'"
)

Peewee

cursor = db.execute_sql(
    "SELECT id, name, email FROM users "
    "WHERE created_at > '2024-01-01'"
)
rows = cursor.fetchall()

Prisma

const users = await prisma.$queryRaw`
  SELECT id, name, email FROM users
  WHERE created_at > '2024-01-01'
`;

Drizzle

import { sql } from "drizzle-orm";

const users = await db.execute(
  sql`SELECT id, name, email FROM users
      WHERE created_at > '2024-01-01'`
);

Kysely

import { sql } from 'kysely'

const rows = await sql<{
  id: number
  name: string
  email: string
}[]>`
  SELECT id, name, email
  FROM users
  WHERE created_at > '2024-01-01'
`.execute(db)

TypeORM

const users = await dataSource.query(
  `SELECT id, name, email FROM users
   WHERE created_at > '2024-01-01'`
);

MikroORM

const rows = await orm.em.getConnection().execute(
  `SELECT id, name, email FROM users
   WHERE created_at > '2024-01-01'`,
);

Sequelize

const [rows] = await sequelize.query(`
  SELECT id, name, email
  FROM users
  WHERE created_at > '2024-01-01'
`);

Raw SQL with Parameters

SQL

-- PostgreSQL positional parameters
SELECT id, name, email
FROM users
WHERE age > $1 AND city = $2;
-- e.g. [25, 'NYC']

Django ORM

from django.db import connection

with connection.cursor() as cursor:
    cursor.execute(
        "SELECT id, name, email FROM users "
        "WHERE age > %s AND city = %s",
        [25, "NYC"],
    )
    rows = cursor.fetchall()

SQLAlchemy

from sqlalchemy import text

with engine.connect() as conn:
    result = conn.execute(
        text(
            "SELECT id, name, email FROM users "
            "WHERE age > :age AND city = :city"
        ),
        {"age": 25, "city": "NYC"},
    )
    rows = result.fetchall()

SQLModel

from sqlalchemy import text

with engine.connect() as conn:
    result = conn.execute(
        text(
            "SELECT id, name, email FROM users "
            "WHERE age > :age AND city = :city"
        ),
        {"age": 25, "city": "NYC"},
    )
    rows = result.fetchall()

Tortoise ORM

from tortoise import connections

conn = connections.get("default")
_, rows = await conn.execute_query(
    "SELECT id, name, email FROM users "
    "WHERE age > $1 AND city = $2",
    [25, "NYC"],
)

Peewee

cursor = db.execute_sql(
    "SELECT id, name, email FROM users "
    "WHERE age > %s AND city = %s",
    (25, "NYC"),
)
rows = cursor.fetchall()

Prisma

const age = 25;
const city = "NYC";

const users = await prisma.$queryRaw`
  SELECT id, name, email FROM users
  WHERE age > ${age} AND city = ${city}
`;

Drizzle

import { sql } from "drizzle-orm";

const age = 25;
const city = "NYC";

const users = await db.execute(
  sql`SELECT id, name, email FROM users
      WHERE age > ${age} AND city = ${city}`
);

Kysely

import { sql } from 'kysely'

const rows = await sql<{
  id: number
  name: string
  email: string
}[]>`
  SELECT id, name, email
  FROM users
  WHERE age > ${25} AND city = ${'NYC'}
`.execute(db)

TypeORM

const users = await dataSource.query(
  `SELECT id, name, email FROM users
   WHERE age > $1 AND city = $2`,
  [25, "NYC"]
);

MikroORM

const rows = await orm.em.getConnection().execute(
  `SELECT id, name, email FROM users
   WHERE age > ? AND city = ?`,
  [25, "NYC"],
);

Sequelize

const [rows] = await sequelize.query(
  `SELECT id, name, email FROM users
   WHERE age > :age AND city = :city`,
  {
    replacements: { age: 25, city: "NYC" },
  },
);

Raw SQL Returning Models

SQL

-- No model layer in raw SQL;
-- application code maps rows to objects
SELECT * FROM users WHERE age > 25;

Django ORM

# .raw() returns User model instances
users = User.objects.raw(
    "SELECT * FROM users WHERE age > %s",
    [25],
)
for user in users:
    print(user.name)  # User instance

SQLAlchemy

from sqlalchemy import select, text

with Session(engine) as session:
    users = session.execute(
        select(User).from_statement(
            text("SELECT * FROM users "
                 "WHERE age > :age")
        ),
        {"age": 25},
    ).scalars().all()
    # Returns User model instances

SQLModel

from sqlalchemy import text
from sqlmodel import select

with Session(engine) as session:
    users = session.exec(
        select(User).from_statement(
            text("SELECT * FROM users WHERE age > :age")
        ).params(age=25)
    ).all()
    # Returns User model instances

Tortoise ORM

# .raw() returns model instances
users = await User.raw(
    "SELECT * FROM users WHERE age > 25"
)
for user in users:
    print(user.name)  # User instance

Peewee

# .raw() returns User model instances
users = User.raw(
    "SELECT * FROM users WHERE age > %s",
    25,
)
for user in users:
    print(user.name)  # User instance

Prisma

// $queryRaw returns typed objects (no classes)
type UserRow = {
  id: number;
  name: string;
  email: string;
};

const users = await prisma.$queryRaw<UserRow[]>`
  SELECT * FROM users WHERE age > ${25}
`;

Drizzle

// Alternative: no built-in raw-to-schema mapping;
// Drizzle returns typed rows from query builder
import { gt } from "drizzle-orm";

const result = await db
  .select()
  .from(users)
  .where(gt(users.age, 25));
// Returns typed rows matching schema

Kysely

import { sql } from 'kysely'

// Kysely has no model instances; returns typed rows.
type UserRow = {
  id: number
  name: string
  email: string
}

const users = await sql<UserRow[]>`
  SELECT * FROM users WHERE age > ${25}
`.execute(db)

TypeORM

// query() returns plain objects;
// map to entities manually
const raw = await dataSource.query(
  "SELECT * FROM users WHERE age > $1",
  [25]
);
const users = raw.map((row) =>
  Object.assign(new User(), row)
);

MikroORM

const rows = await orm.em.getConnection().execute(
  "SELECT * FROM users WHERE age > ?",
  [25],
);

const users = rows.map((row) => orm.em.map(User, row));
// Returns User entity instances

Sequelize

// query() can map rows back to model instances
const users = await sequelize.query(
  "SELECT * FROM users WHERE age > :age",
  {
    replacements: { age: 25 },
    model: User,
    mapToModel: true,
  },
);
for (const user of users) {
  console.log(user.name); // User instance
}