TypeORM Basics
- Never use synchronize: true in production — use TypeORM migrations instead.
- findOne returns null (not undefined) when not found — check explicitly.
- Relations must be explicitly loaded with relations option or joins — TypeORM is lazy by default.
TypeORM is a TypeScript ORM that maps classes to database tables. Define entities as TypeScript classes with decorators. Use DataSource.initialize() to connect. Access repositories with dataSource.getRepository(Entity) for CRUD, or QueryBuilder for complex queries. Supports PostgreSQL, MySQL, SQLite, and more.
Setting Up TypeORM
// data-source.ts import { DataSource } from 'typeorm'; import { User } from './entities/User'; import { Post } from './entities/Post'; export const AppDataSource = new DataSource({ 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 startup await AppDataSource.initialize(); console.log('Database connected');
Defining Entities
// entities/User.ts import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, OneToMany } from 'typeorm'; import { Post } from './Post'; @Entity('users') export class User { @PrimaryGeneratedColumn() id: number; @Column({ unique: true }) email: string; @Column() name: string; @Column({ default: true }) isActive: boolean; @CreateDateColumn() createdAt: Date; @OneToMany(() => Post, (post) => post.author) posts: Post[]; } // entities/Post.ts import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm'; import { User } from './User'; @Entity('posts') export class Post { @PrimaryGeneratedColumn() id: number; @Column() title: string; @Column('text') content: string; @ManyToOne(() => User, (user) => user.posts) @JoinColumn({ name: 'author_id' }) author: User; }
CRUD with Repository
const userRepo = AppDataSource.getRepository(User); // CREATE const user = userRepo.create({ name: 'Alice', email: 'alice@example.com' }); await userRepo.save(user); console.log('Created user:', user.id); // READ const found = await userRepo.findOne({ where: { email: 'alice@example.com' } }); const all = await userRepo.find({ where: { isActive: true } }); // READ with relations const withPosts = await userRepo.findOne({ where: { id: 1 }, relations: { posts: true } }); console.log(withPosts?.posts.length); // UPDATE await userRepo.update({ id: 1 }, { name: 'Alice Smith' }); // DELETE await userRepo.delete({ id: 1 }); // QueryBuilder for complex queries const activeUsers = await userRepo .createQueryBuilder('user') .leftJoinAndSelect('user.posts', 'post') .where('user.isActive = :active', { active: true }) .andWhere('post.id IS NOT NULL') .orderBy('user.createdAt', 'DESC') .getMany();
🎯 Key Takeaways
- Never use synchronize: true in production — use TypeORM migrations instead.
- findOne returns null (not undefined) when not found — check explicitly.
- Relations must be explicitly loaded with relations option or joins — TypeORM is lazy by default.
- QueryBuilder is for complex queries; the Repository API covers 80% of CRUD needs.
- TypeORM supports both Active Record (entity extends BaseEntity) and Data Mapper (repository) patterns.
Interview Questions on This Topic
- QWhat is the difference between
save()andupdate()in TypeORM? - QWhy is synchronize: true dangerous in production?
- QHow do you load related entities in TypeORM?
Frequently Asked Questions
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.
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.
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.