TypeORM maps TypeScript classes to database tables via decorators
DataSource.initialize() establishes the connection pool
Repository API covers 80% of CRUD; QueryBuilder for complex joins
Relations are lazy by default — must explicitly load with 'relations' option
Never use synchronize: true in production — use migrations to avoid data loss
✦ Definition~90s read
What is TypeORM Basics?
TypeORM is an Object-Relational Mapper (ORM) for TypeScript and JavaScript that maps database tables to classes (entities) and lets you query them using TypeScript syntax instead of raw SQL. It supports MySQL, PostgreSQL, SQLite, and others, and is one of the most popular ORMs in the Node.js ecosystem alongside Prisma and Sequelize.
★
TypeORM lets you work with databases using TypeScript classes instead of SQL.
TypeORM's key value is reducing boilerplate for CRUD operations and providing type-safe queries, but it also introduces abstraction layers that can hide dangerous defaults — most notably the synchronize: true flag, which automatically alters your database schema to match your entity definitions on every application startup.
You'd use TypeORM when you want rapid prototyping, a familiar ActiveRecord-like pattern, or deep integration with TypeScript decorators. Avoid it if you need fine-grained control over SQL, work with legacy schemas that don't map cleanly to entities, or prefer a query-builder approach like Knex.js.
The synchronize flag is convenient in development but catastrophic in production — it can drop columns, rename tables, or wipe data without warning, as the article's title warns. For production, you must use TypeORM's migration system, which generates versioned SQL files you review and apply manually.
TypeORM's entity system uses decorators like @Entity(), @Column(), and @PrimaryGeneratedColumn() to define schemas. Its repository pattern (getRepository(User)) provides methods like find(), save(), and delete(). Relations (@ManyToOne, @OneToMany) support eager/lazy loading, but lazy loading often triggers N+1 queries.
The migration CLI (typeorm migration:generate) creates timestamped SQL files from entity changes, letting you evolve schemas safely — the only sane approach for production databases.
Plain-English First
TypeORM lets you work with databases using TypeScript classes instead of SQL. You define a class like User, add some decorators, and TypeORM handles creating tables, inserting data, and querying — all with type safety and autocomplete.
⚙ Browser compatibility
Latest versions — ✓ supported
Chrome
Firefox
Safari
Edge
✓
✓
✓
✓
TypeORM is the most popular ORM for TypeScript/Node.js applications. It follows the Active Record or Data Mapper pattern, letting you interact with your database using TypeScript classes and decorators rather than writing raw SQL.
The main advantage over raw queries: type safety. Refactoring column names propagates through the application with TypeScript compiler errors, not runtime bugs. The main disadvantage: complex queries are often easier to read as SQL than as QueryBuilder chains.
What TypeORM's Synchronize Flag Actually Does
TypeORM's `synchronize: true` is a development convenience that automatically creates database tables and columns based on your entity definitions at application startup. It mirrors your entity schema to the database schema on every application boot, effectively running DDL statements like CREATE TABLE, ALTER TABLE, and ADD COLUMN without explicit migrations.
In practice, this means TypeORM reads your entity decorators (e.g., @Entity, @Column) and generates corresponding SQL DDL. If you add a new column to an entity, synchronize will ALTER TABLE to add it. If you rename a column, synchronize will drop the old column and create a new one — losing all data in that column. It does not perform data migrations; it only ensures the schema structure matches the entity definitions at that moment.
Use `synchronize: true` only in local development or isolated test environments where data loss is acceptable. In any shared environment — staging, QA, or production — disable it and use proper migration scripts. The flag is not a substitute for version-controlled migrations; it is a rapid prototyping tool that becomes a liability the moment you have real data.
⚠ Synchronize is not a migration tool
It drops columns and tables without warning. A renamed entity field becomes a dropped column — data gone, no rollback.
📊 Production Insight
A team enabled synchronize: true in a staging environment shared with QA. A developer renamed a column in the entity, and on next deploy, TypeORM dropped the old column and created a new empty one — wiping 3 months of QA test data. The team had no backup because they assumed staging was transient. Rule: never enable synchronize on any database that holds data you cannot afford to lose, even if it's "just" staging.
🎯 Key Takeaway
Synchronize is for prototyping only — never for environments with persistent data.
It performs destructive schema changes (drop column, drop table) without confirmation.
Always use version-controlled migrations for any schema change in shared or production databases.
thecodeforge.io
Typeorm Basics
Setting Up TypeORM
The DataSource is the central hub of TypeORM — it holds the connection pool and entity registry. You configure it once at startup, usually in a separate file. The synchronize option should always be false in production; use migrations for schema evolution.
data-source.tsTYPESCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { DataSource } from'typeorm';
import { User } from'./entities/User';
import { Post } from'./entities/Post';
exportconstAppDataSource = newDataSource({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'password',
database: 'myapp',
entities: [User, Post],
synchronize: false, // NEVER use true in production — use migrations
logging: ['query', 'error'],
});
// Initialize once at app startupawaitAppDataSource.initialize();
console.log('Database connected');
⚠ Don't forget to close the DataSource during shutdown
If you don't call await AppDataSource.destroy() on app shutdown, you'll leave open connections in the pool, causing a slow leak. Use process.on('SIGTERM', ...) in serverless or containers.
📊 Production Insight
A misconfigured DataSource can take down the whole app if the database is unreachable.
Use a retry mechanism with exponential backoff instead of failing instantly.
Rule: never embed credentials in source code — use environment variables.
🎯 Key Takeaway
DataSource is a singleton — initialize once, reuse everywhere.
Synchronize false is the first line of defense against accidental data loss.
Always pair with a shutdown hook.
Choosing DataSource config approach
IfSingle database, simple app
→
UseUse default config in code. Just set type, host, credentials.
IfMultiple databases or complex pool tuning
→
UseUse ormconfig.json or environment-specific config files.
IfServerless (AWS Lambda, etc.)
→
UseCreate DataSource lazily and cache across invocations — never create per request.
Defining Entities
Entities are TypeScript classes decorated with @Entity. Each entity maps to a database table. Columns are defined with @Column, @PrimaryGeneratedColumn, etc. Relations use @OneToMany, @ManyToOne, @ManyToMany, and @JoinColumn to define foreign keys. Always specify the inverse side for bidirectional relations.
Think of an entity as both the blueprint for the table and the shape of the data you'll work with in your app.
The decorators tell TypeORM what SQL DDL to generate (create table, add foreign key).
The class is used at runtime to hold query results.
Changing a decorator does not alter the database unless you run a migration.
📊 Production Insight
Adding @Column() with a default value does not automatically backfill existing rows.
You must write a migration to ALTER COLUMN SET DEFAULT or update rows manually.
Rule: always verify migration SQL against production data shape.
🎯 Key Takeaway
Entity decorators are declarative schema definitions, not runtime mutations.
Relations need explicit inverse side for TypeORM to manage foreign keys.
Test every new column against real data volume before deploying.
Choosing column types
IfPrimary key with auto-increment
→
UseUse @PrimaryGeneratedColumn()
IfUUID primary key
→
UseUse @PrimaryGeneratedColumn('uuid') or set default in database.
IfTimestamps like createdAt/updatedAt
→
UseUse @CreateDateColumn and @UpdateDateColumn — they auto-set on persist.
thecodeforge.io
Typeorm Basics
CRUD with Repository
The Repository pattern is the simplest way to interact with a single entity. Use dataSource.getRepository(Entity) to get a repository instance. For simple operations like create, read, update, delete, the repository methods are enough. For complex queries — joins, aggregations, subqueries — switch to QueryBuilder.
save() triggers lifecycle hooks and cascades; update() is a direct SQL UPDATE. Use save() for single entity operations that need hooks. Use update() for bulk updates where you don't need events.
📊 Production Insight
findOne returns null when not found — not undefined. Accessing properties without check throws TypeError.
The relations option triggers an extra JOIN per relation — limit to only those you need.
Rule: always check for null after findOne and use relations: { ... } selectively.
🎯 Key Takeaway
Repository API is your default for CRUD — fast, typed, and straightforward.
Switch to QueryBuilder when you need WHERE on related tables or aggregation.
Never nest find calls inside loops — that's the N+1 problem.
Choosing between Repository and QueryBuilder
IfSimple CRUD on one table
→
UseUse Repository API (find, save, delete).
IfComplex query with joins, subqueries, or aggregations
→
UseUse createQueryBuilder.
IfNeed raw SQL performance or database-specific features
→
UseUse dataSource.query('SELECT ...') — but lose type safety.
Relations: Loading Strategies and Pitfalls
TypeORM relations are lazy by default — they don't load related entities unless you explicitly ask for them. You can load relations via the 'relations' option in find/findOne, or via leftJoinAndSelect in QueryBuilder. Eager relations (set with { eager: true }) load automatically but can cause N+1 if used carelessly. The biggest production pitfall: assuming relations are always loaded.
⚠ Eager loading on OneToMany can be a performance trap
If you mark a OneToMany relation as eager, every find() on that entity will load all related records — even when you don't need them. This can easily become the source of slow queries. Always measure the actual query log.
📊 Production Insight
N+1 queries happen when you iterate over a collection and access a lazy relation inside the loop.
The fix: use leftJoinAndSelect in a single query instead of separate queries per item.
Rule: profile your API endpoints — if response time grows linearly with data volume, you likely have N+1.
🎯 Key Takeaway
Relations are lazy — always load explicitly.
Eager loading is convenient but dangerous on OneToMany.
Use leftJoinAndSelect for complex filtering across relations.
When to use eager vs lazy relations
IfRelation always needed (e.g., User -> Profile)
→
UseConsider eager: true on the owning side.
IfRelation rarely needed (e.g., User -> many Posts)
→
UseKeep lazy; load on demand with relations option.
IfNeed to filter on related table properties
→
UseUse QueryBuilder with leftJoinAndSelect.
Migrations: Safe Schema Evolution
Migrations are the only safe way to change your database schema in production. TypeORM's migration system generates SQL files based on entity changes. Always review the generated SQL before running. Use migration:generate to create a migration, then migration:run to apply. Never edit migration files manually unless you understand the full impact.
migration-commands.shBASH
1
2
3
4
5
6
7
8
9
10
11
# Generate a migration after changing entities
npx typeorm migration:generate -d src/data-source.ts src/migrations/AddEmailVerifiedColumn
# Run pending migrations
npx typeorm migration:run -d src/data-source.ts
# Revert last migration
npx typeorm migration:revert -d src/data-source.ts
# Show status
npx typeorm migration:show -d src/data-source.ts
Mental Model
Migrations = Version control for your database
Just as you wouldn't push code without code review, you shouldn't apply schema changes without reviewing the SQL.
Each migration is a timestamped file that contains up and down methods.
TypeORM compares current entity definitions against the database to generate the diff.
Always verify the generated SQL — it may drop columns or tables unintendedly.
📊 Production Insight
Running multiple migrations in sequence can cause locking on large tables.
Always test migrations on a staging copy of production data first.
Rule: keep migrations small and atomic — one change per migration file.
🎯 Key Takeaway
Migrations are your safety net — use them, never synchronize.
Always review generated SQL before applying.
Test migrations on staging with production-like data volume.
When to run migrations
IfAdding a new column
→
UseGenerate migration, manually add ALTER COLUMN SET DEFAULT if needed.
IfRemoving a column
→
UseEnsure no application code references it first; migration will DROP COLUMN.
IfChanging column type
→
UseRequires careful handling: may need USING clause. Always test on staging.
Who the Hell Is This For? (Audience)
You're already debugging a JOIN that's pulling 40MB of JSON into an Express route at 2 AM. That's who this is for.
TypeORM isn't a toy. It's an ORM that'll happily let you shoot your own foot if you treat it like a magical black box. This blog is for the developer who's been burned by an ORM before — someone who wants to know why synchronize: true just wrote 47 unintended indexes to production, not just how to flip the flag.
If you're still writing findOne with no where clause and hoping for the best, close the tab. Come back when you've cleaned up a migration conflict at 3 AM. Otherwise, keep reading — we're about to talk about the parts of TypeORM that the docs skip because they'd rather sell you a happy story.
🎯 Key Takeaway
This blog assumes you've already broken something with an ORM before. If you haven't, you will — read the warnings.
What You’d Better Know Before Touching This Shit (Prerequisites)
TypeORM is a thin wrapper over SQL and TypeScript. If you can't write a raw SQL JOIN without panicking, you're going to have a bad time. Before you start wiring up entities and decorators, nail these:
TypeScript basics — generics, decorators, async/await. If typeof vs instanceof confuses you, go read the handbook.
SQL fundamentals — SELECT, INSERT, UPDATE, DELETE, and what an index actually does. TypeORM generates SQL; you need to read the query log to know when it's generating garbage.
Relational database design — normalisation, foreign keys, cascade deletes (and why they're a trap). The ORM will enforce relations only if you let it.
Node.js runtime — module resolution, transpilation, process lifecycle. TypeORM runs in Node, so know your event loop from your callback queue.
If you're missing any of these, save yourself the headache. Install SQLite, write a few raw queries, then come back. You'll thank me when your first migration doesn't nuke the user table.
PrerequisitesCheck.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// io.thecodeforge — database tutorial
-- Can you read this query and predict its execution plan?SELECT
u.id,
u.email,
o.total_amount,
o.created_at
FROM users u
LEFTJOIN orders o ON o.user_id = u.id
WHERE o.created_at > '2024-01-01'AND u.is_active = true
ORDERBY o.total_amount DESC;
-- If not, go learn SQL first. TypeORM hides complexity, not competence.
Output
-- (No output — this is a self-test. If you can't reason about the query, you're not ready.)
⚠ Production Trap:
TypeORM's query builder lets you stack .where() calls that override each other silently. If you don't understand SQL, you'll never catch it until the wrong data leaks into a report.
🎯 Key Takeaway
The ORM is a tool for productive devs, not a crutch for SQL-illiterate ones. Know the underlying database or prepare for pain.
Operators in TypeORM Query Builder
TypeORM's Query Builder gives you fine-grained control over SQL operators. Without them, you're stuck with basic equality queries. Operators let you filter by patterns (LIKE), ranges (BETWEEN), inclusion (IN), and null checks (IS NULL). Each operator maps directly to a Query Builder method: .where('age BETWEEN :min AND :max') or .andWhere('name LIKE :name'). The key insight: operator methods return the same query builder, so you chain them. Use :param placeholders instead of string interpolation to prevent injection. Start with Where, add AndWhere or OrWhere for compound conditions. The NOT operator inverts: .notBrackets() wraps exclusions. Logical operators combine with brackets for precedence. This replaces writing raw SQL strings while keeping full expressiveness.
OperatorsExample.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// io.thecodeforge — database tutorial
-- TypeORM Query Builder operatorsWHERE age BETWEEN :minAge AND :maxAge
AND name LIKE :searchPattern
AND role IN (:...roles)
AND deletedAt ISNULLAND (status = 'active'OR status = 'pending')
// Parameterized: safer than string concat
.createQueryBuilder('user')
.where('user.age BETWEEN :min AND :max', { min: 18, max: 65 })
.andWhere('user.name LIKE :name', { name: '%John%' })
.andWhere('user.role IN (:...roles)', { roles: ['admin', 'mod'] })
Output
Returns filtered records from user table
⚠ Production Trap:
Never embed user input directly into operator strings. Always use parameterized :param syntax to avoid SQL injection—TypeORM will escape values.
🎯 Key Takeaway
Use parameterized operator methods (andWhere, orWhere) with named placeholders—never raw interpolation.
Working Directory Gotchas with TypeOrm
TypeORM resolves entity paths, migration files, and config relative to the process working directory—not your project root. Run npm run from a subfolder? TypeORM might fail to find entity/*.ts. Fix it: set process.env.NODE_PATH or use absolute paths in ormconfig.ts. The root directory determines where TypeORM writes migration files and reads entities. Migration g:co generates files in the current working directory. If your CI runs from a different directory, migrations won't match. Always store TypeORM config with __dirname-based paths to decouple from runtime cwd. Test by running commands from random directories. Common failure: No entity metadata found because entity glob resolves to a wrong relative path.
WorkingDirFix.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// io.thecodeforge — database tutorial
-- Use __dirname for reliable path resolution
import { DataSource } from 'typeorm';
import path from 'path';
export const AppDataSource = new DataSource({
type: 'postgres',
entities: [path.join(__dirname, 'entity/*.{ts,js}')],
migrations: [path.join(__dirname, 'migration/*.{ts,js}')],
// Never rely on relative './' — fails on different cwd
});
// Test: run from /app/src/ but cwd is /app/
// With above: 'entity/*.ts' resolves correctly
Output
DataSource initialized with absolute entity paths
⚠ Production Trap:
If migration files appear in unexpected folders, your cwd is off. Pin with __dirname or set NODE_PATH explicitly in all launch scripts.
🎯 Key Takeaway
Always resolve file paths with __dirname in TypeORM config to survive working directory changes.
thecodeforge.io
Typeorm Basics
Importing from TypeORM Correctly
TypeORM exports dozens of decorators, functions, and types. Importing the wrong thing causes cryptic errors. Core rule: Entity, Column, PrimaryGeneratedColumn come from typeorm main package. Repository methods (find, save, delete) live on Repository type, also from typeorm. The biggest trap: importing EntityRepository (deprecated) instead of using @Injectable() in NestJS or dataSource.getRepository(). For Query Builder, import SelectQueryBuilder from typeorm only when you need its type. For runtime usage, the actual object is returned by createQueryBuilder(). Avoid star imports (import * as ...) — they pull everything. Use named imports with only what you need: import { Entity, Column, BaseEntity } from 'typeorm'.
Importing EntityRepository or getRepository (deprecated in 0.3) will fail silently or throw runtime errors. Always use dataSource.getRepository().
🎯 Key Takeaway
Use targeted named imports from 'typeorm' and dataSource.getRepository() for runtime repository access.
● Production incidentPOST-MORTEMseverity: high
Synchronize: True Wiped a Production Table
Symptom
Users reported missing 'created_at' values on all records created before the deployment. The column simply vanished from the table.
Assumption
The team assumed synchronize would only add missing columns, not remove existing ones. They didn't review the generated SQL.
Root cause
TypeORM's synchronize option compares entity schemas to the database and executes DDL to make them match — including DROP COLUMN for removed decorators. No confirmation prompt.
Fix
1. Restore the column from a backup. 2. Set synchronize: false in all DataSource configs. 3. Move to a migration workflow: generate, review, then run.
Key lesson
Never use synchronize: true in any environment connected to real data.
Always review migration SQL before applying — even in staging.
Use timestamp columns carefully — they are often removed first during refactors.
Production debug guideSymptom → Action guide for common TypeORM failures5 entries
Symptom · 01
App fails to start: Connection refused / timeout
→
Fix
Check database host, port, and network ACLs. Verify DataSource credentials. Test with a simple TCP connection using telnet or nc.
Symptom · 02
Queries return empty relations (null instead of data)
→
Fix
Ensure relations are explicitly loaded via 'relations' option or leftJoinAndSelect. TypeORM does not auto-join relations.
Symptom · 03
Slow queries even with indexes
→
Fix
Enable query logging: logging: ['query', 'error']. Analyze the SQL output in PostgreSQL slow query log or EXPLAIN ANALYZE.
Symptom · 04
TypeError: Cannot read properties of undefined (reading 'id')
→
Fix
Check that findOne returns null (not undefined) — use optional chaining or if (result) before accessing properties.
Symptom · 05
Memory leak over time
→
Fix
Ensure every QueryRunner is released after use. Use getRepository and QueryBuilder which auto-manage connections. Avoid creating DataSource instances per request.
★ Quick Debug Commands for TypeORMRun these commands when things go sideways.
Missing table or column after migration−
Immediate action
Run migration:show to see pending migrations. Check if migration was applied.
What You’d Better Know Before Touching This Shit (Prerequisi
OperatorsExample.sql
WHERE age BETWEEN :minAge AND :maxAge
Operators in TypeORM Query Builder
WorkingDirFix.sql
export const AppDataSource = new DataSource({
Working Directory Gotchas with TypeOrm
ImportExample.sql
Entity,
Importing from TypeORM Correctly
Key takeaways
1
Never use synchronize
true in production — use TypeORM migrations instead.
2
findOne returns null (not undefined) when not found
check explicitly.
3
Relations must be explicitly loaded with relations option or joins
TypeORM is lazy by default.
4
QueryBuilder is for complex queries; the Repository API covers 80% of CRUD needs.
5
TypeORM supports both Active Record (entity extends BaseEntity) and Data Mapper (repository) patterns.
Common mistakes to avoid
5 patterns
×
Using synchronize: true in production
Symptom
Tables get dropped or columns deleted unexpectedly upon deployment. Data loss occurs silently.
Fix
Set synchronize: false in all DataSource configs. Generate and run migrations instead.
×
Not loading relations explicitly
Symptom
Properties like user.posts are undefined even though the relation exists. Code crashes with TypeError when accessing nested data.
Fix
Always include relations: { ... } in find/findOne calls, or use leftJoinAndSelect in QueryBuilder.
×
Creating DataSource per request (especially in serverless)
Symptom
Connection pool exhaustion, slow cold starts, or connection limit errors from the database.
Fix
Create a single global DataSource instance and reuse it across requests. In serverless, cache it outside the handler.
×
Assuming save() always performs INSERT
Symptom
When an entity has an id field set, save() performs an UPDATE instead of INSERT, potentially overwriting existing data.
Fix
Use insert() for new records if you want to guarantee INSERT. Or ensure id is undefined/omitted when creating new entities.
×
Ignoring the N+1 query problem
Symptom
Page load times increase linearly with number of parent records. Database gets hammered with hundreds of small queries.
Fix
Use leftJoinAndSelect or relations option to eager load needed relations in a single query. Monitor query logs regularly.
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01SENIOR
What is the difference between save() and update() in TypeORM?
Q02JUNIOR
Why is synchronize: true dangerous in production?
Q03JUNIOR
How do you load related entities in TypeORM?
Q04SENIOR
What is the N+1 problem in TypeORM and how do you solve it?
Q05SENIOR
How do you handle database migrations in TypeORM in a team environment?
Q01 of 05SENIOR
What is the difference between save() and update() in TypeORM?
ANSWER
save() is a full entity lifecycle method: it checks if the entity has an id, and either inserts or updates accordingly. It also triggers lifecycle hooks (@BeforeInsert, @AfterUpdate, etc.) and cascades. update() is a direct SQL UPDATE that only sets the provided columns — no lifecycle hooks, no cascade, and it skips validation. Use save() for normal CRUD where hooks matter; use update() for bulk updates where performance is critical.
Q02 of 05JUNIOR
Why is synchronize: true dangerous in production?
ANSWER
synchronize: true tells TypeORM to alter the database schema automatically to match your entities every time the application starts. This includes operations like DROP COLUMN, DROP TABLE, and ALTER COLUMN — which can permanently destroy data. There is no confirmation prompt. The fix is to set synchronize: false and use TypeORM migrations, which generate safe, reviewable SQL files.
Q03 of 05JUNIOR
How do you load related entities in TypeORM?
ANSWER
You have three options: 1) Use the 'relations' option in find/findOne: userRepo.findOne({ relations: ['posts'] }). 2) Use QueryBuilder with leftJoinAndSelect: createQueryBuilder('user').leftJoinAndSelect('user.posts', 'post'). 3) Set { eager: true } on the relation decorator, which loads automatically on every find — but beware of N+1 risks. The most flexible approach is QueryBuilder, as it allows filtering on related columns.
Q04 of 05SENIOR
What is the N+1 problem in TypeORM and how do you solve it?
ANSWER
The N+1 problem occurs when you fetch a list of parent entities, then iterate over them to access a lazy-loaded relation, causing one query for the parents and N additional queries for the children. For example, loading 100 users and then accessing user.posts in a loop fires 1 + 100 queries. The solution: use eager loading (leftJoinAndSelect in QueryBuilder or the 'relations' option) to fetch children in the same query. Always check query logs to detect N+1.
Q05 of 05SENIOR
How do you handle database migrations in TypeORM in a team environment?
ANSWER
Generate migrations using the CLI: typeorm migration:generate -d ... name. This creates a timestamped file with up() and down() methods. Review the generated SQL in code review — never assume it's correct. Run migrations via CI/CD pipeline using typeorm migration:run. Avoid editing migration files manually after generation. Use migration:revert to roll back if needed. Always test migrations on a staging environment with production-like data before applying to production.
01
What is the difference between save() and update() in TypeORM?
SENIOR
02
Why is synchronize: true dangerous in production?
JUNIOR
03
How do you load related entities in TypeORM?
JUNIOR
04
What is the N+1 problem in TypeORM and how do you solve it?
SENIOR
05
How do you handle database migrations in TypeORM in a team environment?
SENIOR
FAQ · 5 QUESTIONS
Frequently Asked Questions
01
What is the difference between save() and update() in TypeORM?
save() handles both insert and update — if the entity has an id, it updates; if not, it inserts. It also triggers lifecycle hooks and cascades. update() is a direct SQL UPDATE — no lifecycle hooks, no cascade, but faster for bulk updates. Use save() for normal CRUD; use update() when performance matters or hooks are not needed.
Was this helpful?
02
Why should I never use synchronize: true in production?
synchronize: true automatically alters the database schema to match your entities on every application start. It can DROP columns that are removed from entities — losing data. Use TypeORM migrations instead: generate migration files, review them, and apply them deliberately.
Was this helpful?
03
How do I fix the 'No metadata for entity' error?
This error means TypeORM cannot find your entity classes. Ensure all entities are listed in the 'entities' array of your DataSource config, or use glob patterns like 'src/entities/*/.ts'. Also verify that the entity class is exported and decorated with @Entity.
Was this helpful?
04
Can I use TypeORM with multiple databases?
Yes. Create multiple DataSource instances, each configured for a different database. You can then use getRepository() on the appropriate DataSource. Note that relations across DataSources are not supported — you'll need to load related entities manually.
Was this helpful?
05
What is the difference between find, findOne, and findOneOrFail?
find() returns an array of entities (empty array if none). findOne() returns the entity or null. findOneOrFail() returns the entity or throws an EntityNotFoundError. Use findOneOrFail when the entity must exist — saves an explicit check.