ORM
Object-Relational Mapping: a library that lets you interact with a database using your programming language's objects instead of raw SQL. Drizzle, Prisma, and SQLAlchemy are ORMs.
What is ORM?
In short
An ORM (Object-Relational Mapping) is a library that lets you read and write a relational database using objects, classes, and methods in your programming language instead of writing raw SQL by hand. You define a model like User in code, and the ORM translates your calls (User.find, user.save) into the SQL the database actually runs.
What an ORM actually is
A relational database speaks SQL and stores data in rows and columns. Your application speaks objects: a User has a name and an email, an Order has a total and a list of items. An ORM sits between the two and maps tables to classes, rows to object instances, and columns to fields. That mapping is where the name comes from.
Instead of writing SELECT * FROM users WHERE id = 42, you write something like User.findById(42) and get back a User object. Instead of building an INSERT statement and remembering the column order, you create an object and call save(). The ORM generates the SQL, sends it over the database driver, reads the result rows, and hands you back typed objects.
Popular ORMs cover most languages: SQLAlchemy and Django ORM for Python, Hibernate and JPA for Java, ActiveRecord for Ruby on Rails, Entity Framework for .NET, and Prisma, Drizzle, and TypeORM for TypeScript. They differ a lot in style but solve the same core problem.
How it works under the hood
You start by declaring models. In Prisma you write a schema file; in SQLAlchemy or Django you write Python classes; in Drizzle you write TypeScript objects that describe each table and column. The ORM uses this definition both to validate your code and to know how to build queries.
When you call something like db.query.users.findMany({ where: { active: true } }), the ORM builds an abstract query, compiles it to a SQL string with bound parameters, and sends it through a connection from a pool. Parameters are bound separately from the SQL text, which is why ORMs give you protection against SQL injection almost for free.
Many heavier ORMs (Hibernate, SQLAlchemy, ActiveRecord) also track loaded objects in a session or identity map, batch changes, and flush them as a transaction when you commit. Lighter query builders like Drizzle skip most of that and stay close to the SQL you would have written, trading magic for predictability.
Most ORMs also ship migrations. You change a model, generate a migration file that holds the ALTER TABLE statements, and run it to evolve the schema in a versioned, repeatable way across dev, staging, and production.
When to use one and the trade-offs
Use an ORM when you want fewer hand-written queries, type safety between your code and your schema, and easy migrations. For standard CRUD work, an ORM removes a huge amount of boilerplate and catches mistakes at compile time rather than at runtime in production.
The classic trap is the N+1 query problem. Loop over 100 users and access user.posts inside the loop, and a naive ORM fires 1 query for the users plus 100 more for the posts, 101 round trips where a single JOIN would do. You fix this with eager loading or explicit joins, but you have to know it is happening.
The other trade-off is the leaky abstraction. ORMs hide SQL right up to the point where you need a window function, a recursive CTE, or a finely tuned index hint, and then you drop down to raw SQL anyway. Good ORMs make that escape hatch easy. The honest guidance: let the ORM handle the 90 percent of simple queries and write raw SQL for the hot, complex 10 percent.
A concrete example
Say you run an e-commerce app on PostgreSQL. With raw SQL you would write a parameterized INSERT for every new order, manually map result rows back into structs, and update column lists by hand every time the schema changes.
With an ORM like Prisma you write prisma.order.create({ data: { userId, total, items } }) and it generates the INSERT, runs it inside a transaction if items span multiple tables, and returns a fully typed Order object. Change the schema by editing the model, run prisma migrate, and the generated client updates its types so a forgotten field is now a compile error.
Shopify runs on Rails ActiveRecord at enormous scale, and Instagram runs on the Django ORM. Both prove an ORM can carry a serious production workload, as long as the team watches query counts and reaches for raw SQL on the few queries that need it.
Where it is used in production
Shopify
Built on Ruby on Rails and its ActiveRecord ORM, serving millions of merchant stores on a heavily sharded MySQL backend.
Runs on Django, using the Django ORM to talk to PostgreSQL across one of the largest Django deployments in the world.
Prisma
A widely used TypeScript ORM that generates a fully typed client from a schema file and ships its own migration engine.
Hibernate
The dominant Java ORM and reference implementation of JPA, used across enterprise backends with session caching and lazy loading.
Frequently asked questions
- What is the difference between an ORM and a query builder?
- A query builder (like Knex or the core of Drizzle) gives you a fluent API that maps almost one to one onto SQL clauses, so you still think in terms of select, join, and where. A full ORM adds object mapping, change tracking, relationships, and lazy loading on top, so you think in terms of objects. Query builders are more predictable; full ORMs do more for you but hide more.
- Does an ORM protect against SQL injection?
- Mostly yes. Because ORMs send your values as bound parameters separate from the SQL text, untrusted input cannot change the query structure. The exception is when you drop to raw SQL and interpolate user input into the string yourself. Use the ORM's parameter binding even in raw queries and you stay safe.
- What is the N+1 query problem?
- It is the most common ORM performance bug. You fetch a list of records, then access a related field in a loop, and the ORM silently fires one extra query per record. Fetching 100 users and their posts becomes 101 queries instead of 1 or 2. The fix is eager loading: tell the ORM to load the relation up front with a join or a single batched query.
- Are ORMs slower than raw SQL?
- There is a small overhead for building and parsing queries, but for normal workloads it is negligible compared to network round trips and disk reads. The real slowdowns come from bad query patterns like N+1, over-fetching columns you do not need, or missing indexes, all of which you can hit with raw SQL too. Profile the generated SQL and the ORM is rarely the bottleneck.
- Can I still write raw SQL when using an ORM?
- Yes, and you should for complex queries. Every serious ORM has an escape hatch for raw SQL with safe parameter binding. The common pattern is to let the ORM handle routine CRUD and write raw SQL for reports, window functions, recursive queries, and anything that needs careful index tuning.
Learn ORM hands-on
This page explains the idea. The full lesson lets you step through the ring as servers join and leave, read the implementation, and check yourself with a quiz. It is one of 760+ lessons in the System Design Masterclass, from your first API call to distributed consensus. Eleven Foundation lessons are free, no signup. Lifetime access is ₹499 in India or $7.99 worldwide, one payment, no subscription.
Related lessons
Lessons that touch on ORM as part of a larger topic.
Database Denormalization
Intentionally adding redundancy for read performance, when breaking the rules is the right call
foundation · database fundamentals
Data Transformation
Convert data from one format, structure, or representation to another, the glue between incompatible systems
intermediate · data governance compliance
Performance Testing
Measuring and optimizing response times, throughput, and resource usage to meet performance SLAs
intermediate · devops cicd
Normalizer Pattern
Convert messages from different formats into a common structure before processing
intermediate · messaging event systems
Image Formats
JPEG, PNG, WebP, AVIF, SVG: understanding when to use each format and why it matters for performance
intermediate · web content delivery
See also
Related glossary terms you might want to look up next.
SQL
Structured Query Language for managing relational databases. Tables, rows, columns, and powerful joins to query related data.
Database
An organized collection of data that can be easily accessed, managed, and updated. The backbone of almost every application.
Connection Pool
A cache of reusable database connections that avoids the overhead of opening and closing a new connection for every query. Critical for high-throughput applications.
NoSQL
Databases that don't use traditional table-based relational models. Includes document stores, key-value, graph, and column-family databases.
ACID
Four guarantees for database transactions: Atomicity (all or nothing), Consistency (valid states only), Isolation (no interference), Durability (changes persist).
BASE
An alternative to ACID for distributed systems: Basically Available, Soft state, Eventually consistent. Trades strong consistency for availability.