ORM
ORM (Object-Relational Mapping) is a technique that lets developers interact with a relational database using object-oriented code instead of writing SQL directly. It maps database tables to classes and rows to objects.
§ 1 Definition
ORM (Object-Relational Mapping) is a programming technique that bridges the relational database world (tables, rows, columns, SQL) with the object-oriented programming world (classes, objects, methods, properties). An ORM library generates SQL from your object-oriented code and maps query results back to objects. Popular ORMs include Prisma (TypeScript/Node.js), SQLAlchemy (Python), Sequelize (Node.js), TypeORM (TypeScript), Eloquent (PHP/Laravel), Entity Framework (.NET), Hibernate (Java), and Django ORM (Python). ORMs provide significant productivity benefits: you write code in your application's language instead of switching to SQL, you get type safety and autocomplete for database operations, migrations are managed in code, and the ORM handles connection pooling and query parameterization (preventing SQL injection). The tradeoff is a leaky abstraction: ORMs generate SQL, and that generated SQL is sometimes inefficient. A query that would be a simple JOIN in SQL becomes an N+1 disaster in the ORM. Understanding the generated SQL is essential for production use.
§ 2 How ORMs Work
An ORM maps database tables to model classes. Each table becomes a class. Each row becomes an instance of that class. Each column becomes a property of the instance. Relationships (foreign keys) become object references or collections. The ORM translates method calls to SQL queries. For example, User.findByEmail('[email protected]') generates SELECT * FROM users WHERE email = '[email protected]' LIMIT 1 and returns a User object with properties matching the row columns. Query builders (a subset of ORMs) let you construct SQL-like queries programmatically without writing raw SQL strings. Full ORMs also include identity maps (caching already-loaded objects), change tracking (detecting which fields have been modified), lazy loading (loading related objects only when accessed), and eager loading (fetching related objects in the same query).
§ 3 The N+1 Problem
The N+1 query problem is the most common ORM performance trap. It happens when your code loads a list of parent records, then accesses a relationship for each one, triggering a separate query per parent. Example: loading 100 users, then accessing user.posts for each user triggers 101 queries (1 for users, 100 for posts). The ORM's lazy-loading defaults cause this. The fix is eager loading: telling the ORM to fetch the related data in a JOIN upfront. In Prisma: include: { posts: true }. In SQLAlchemy: .options(joinedload(User.posts)). N+1 queries are hard to catch in development (they work fine with 5 test records) and devastating in production (100 users * 10 posts = 1,000+ queries per page load). Monitoring query counts in development is essential.
§ 4 ORM vs Raw SQL: When to Use Each
ORMs excel at standard CRUD operations, simple queries, and rapid development. They reduce boilerplate, prevent SQL injection, and provide type safety. Use an ORM for 80% of your application's database interactions. Write raw SQL for the remaining 20%: complex reports with aggregations, recursive CTEs, bulk operations that need optimizing, database-specific features (PostgreSQL's full-text search, JSON operators, window functions), and performance-critical queries where the ORM generates suboptimal SQL. The best approach is 'ORM-first, raw-SQL-when-needed.' Prisma supports raw queries, SQLAlchemy has text(), and Django has raw(). Good ORMs make it easy to drop down to SQL when the abstraction is not serving you.
§ 5 Note
§ 6 In code
```typescript
// Prisma ORM (TypeScript)
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// ORM-style query (generates SQL automatically)
const user = await prisma.user.findUnique({
where: { email: '[email protected]' },
include: {
posts: {
orderBy: { createdAt: 'desc' },
take: 5,
},
},
});
// Raw SQL when ORM is not enough
const topAuthors = await prisma.$queryRaw`
SELECT u.name, COUNT(p.id) as post_count
FROM users u
JOIN posts p ON p.author_id = u.id
WHERE p.created_at >= NOW() - INTERVAL '30 days'
GROUP BY u.id, u.name
ORDER BY post_count DESC
LIMIT 10
`;
```§ 7 Common questions
- Q. Should I use an ORM for my project?
- A. Yes, for almost every project. ORMs significantly accelerate development, provide type safety, prevent SQL injection for parameterized queries, and handle migrations. Drop to raw SQL when the ORM generates inefficient queries.
- Q. Which ORM should I choose?
- A. TypeScript/Node.js: Prisma (modern, type-safe, excellent schema management). Python: SQLAlchemy (most powerful, mature) or Django ORM (if using Django). PHP: Eloquent (Laravel). .NET: Entity Framework Core.
- Q. What is the N+1 problem?
- A. Loading a list of N parent records, then accessing a relationship for each one, causing N additional queries. Fix with eager loading (JOIN-based fetching). Always monitor query counts.
- ORM maps database tables to classes and rows to objects, reducing boilerplate and preventing SQL injection.
- ORMs excel at standard CRUD (80% of queries). Use raw SQL for complex reports and performance-critical operations (20%).
- The N+1 problem is the most common ORM performance trap. Use eager loading to prevent it.
- ORMs do not eliminate the need to understand SQL. You must read and optimize generated queries.
Atomic Glue uses ORMs on every project to accelerate development and maintain code quality. Prisma is our standard for TypeScript/Node.js backends, SQLAlchemy for Python, and Eloquent for PHP/Laravel. We understand the abstractions well enough to optimize generated SQL, prevent N+1 queries, and drop to raw SQL when needed. ORM expertise is part of our standard Web Development services. Get in touch to build efficient, maintainable database layers.
Get in touchORM (Object-Relational Mapping) is a technique that lets developers interact with a relational database using object-oriented code instead of writing SQL directly. It maps database tables to classes and rows to objects.
Category: Backend (also: Software Engineering)
Author: Atomic Glue Development Team
## Definition
ORM (Object-Relational Mapping) is a programming technique that bridges the relational database world (tables, rows, columns, SQL) with the object-oriented programming world (classes, objects, methods, properties). An ORM library generates SQL from your object-oriented code and maps query results back to objects. Popular ORMs include Prisma (TypeScript/Node.js), SQLAlchemy (Python), Sequelize (Node.js), TypeORM (TypeScript), Eloquent (PHP/Laravel), Entity Framework (.NET), Hibernate (Java), and Django ORM (Python). ORMs provide significant productivity benefits: you write code in your application's language instead of switching to SQL, you get type safety and autocomplete for database operations, migrations are managed in code, and the ORM handles connection pooling and query parameterization (preventing SQL injection). The tradeoff is a leaky abstraction: ORMs generate SQL, and that generated SQL is sometimes inefficient. A query that would be a simple JOIN in SQL becomes an N+1 disaster in the ORM. Understanding the generated SQL is essential for production use.
## How ORMs Work
An ORM maps database tables to model classes. Each table becomes a class. Each row becomes an instance of that class. Each column becomes a property of the instance. Relationships (foreign keys) become object references or collections. The ORM translates method calls to SQL queries. For example, User.findByEmail('[email protected]') generates SELECT * FROM users WHERE email = '[email protected]' LIMIT 1 and returns a User object with properties matching the row columns. Query builders (a subset of ORMs) let you construct SQL-like queries programmatically without writing raw SQL strings. Full ORMs also include identity maps (caching already-loaded objects), change tracking (detecting which fields have been modified), lazy loading (loading related objects only when accessed), and eager loading (fetching related objects in the same query).
## The N+1 Problem
The N+1 query problem is the most common ORM performance trap. It happens when your code loads a list of parent records, then accesses a relationship for each one, triggering a separate query per parent. Example: loading 100 users, then accessing user.posts for each user triggers 101 queries (1 for users, 100 for posts). The ORM's lazy-loading defaults cause this. The fix is eager loading: telling the ORM to fetch the related data in a JOIN upfront. In Prisma: include: { posts: true }. In SQLAlchemy: .options(joinedload(User.posts)). N+1 queries are hard to catch in development (they work fine with 5 test records) and devastating in production (100 users * 10 posts = 1,000+ queries per page load). Monitoring query counts in development is essential.
## ORM vs Raw SQL: When to Use Each
ORMs excel at standard CRUD operations, simple queries, and rapid development. They reduce boilerplate, prevent SQL injection, and provide type safety. Use an ORM for 80% of your application's database interactions. Write raw SQL for the remaining 20%: complex reports with aggregations, recursive CTEs, bulk operations that need optimizing, database-specific features (PostgreSQL's full-text search, JSON operators, window functions), and performance-critical queries where the ORM generates suboptimal SQL. The best approach is 'ORM-first, raw-SQL-when-needed.' Prisma supports raw queries, SQLAlchemy has text(), and Django has raw(). Good ORMs make it easy to drop down to SQL when the abstraction is not serving you.
## Note
Common misunderstanding: ORMs eliminate the need to understand SQL. They do not. ORMs are abstractions, and all abstractions leak. You must understand what SQL your ORM generates to debug performance issues, optimize queries, and handle complex operations. Another misconception: ORMs are always slower than raw SQL. For standard CRUD operations, the difference is negligible. The ORM overhead (object construction, change tracking) is microseconds per query, invisible compared to network latency and database execution time. The real risk is ORM-generated SQL that is structurally inefficient (N+1 queries, missing indexes, unnecessary joins). Also: ORMs do not prevent all SQL injection. Raw query methods in ORMs can still be vulnerable if you interpolate user input directly.
## In code
## Common questions Q: Should I use an ORM for my project? A: Yes, for almost every project. ORMs significantly accelerate development, provide type safety, prevent SQL injection for parameterized queries, and handle migrations. Drop to raw SQL when the ORM generates inefficient queries. Q: Which ORM should I choose? A: TypeScript/Node.js: Prisma (modern, type-safe, excellent schema management). Python: SQLAlchemy (most powerful, mature) or Django ORM (if using Django). PHP: Eloquent (Laravel). .NET: Entity Framework Core. Q: What is the N+1 problem? A: Loading a list of N parent records, then accessing a relationship for each one, causing N additional queries. Fix with eager loading (JOIN-based fetching). Always monitor query counts.
## Key takeaways
- ORM maps database tables to classes and rows to objects, reducing boilerplate and preventing SQL injection.
- ORMs excel at standard CRUD (80% of queries). Use raw SQL for complex reports and performance-critical operations (20%).
- The N+1 problem is the most common ORM performance trap. Use eager loading to prevent it.
- ORMs do not eliminate the need to understand SQL. You must read and optimize generated queries.
## Related entries
- [Database](atomicglue.co/glossary/database)
- [SQL](atomicglue.co/glossary/sql)
- [Node.js](atomicglue.co/glossary/node-js)
- [Python](atomicglue.co/glossary/python)
- [PHP](atomicglue.co/glossary/php)
Last updated June 2026. Permalink: atomicglue.co/glossary/orm