Relations: posts and comments
A relation describes how rows of one table relate to rows of another. One post can have many comments, and each comment belongs to one post — this is a one-to-many relation. TypeORM defines it with the @OneToMany() and @ManyToOne() decorators and adds a column referencing the post to the comments table itself.
The comments resource
nest g resource commentsChoose REST API and answer n to the CRUD entry points question: comment routes are nested inside the post address, and they are easier to write by hand.
The comment entity
Create src/comments/entities/comment.entity.ts:
// src/comments/entities/comment.entity.ts
import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { Post } from '../../posts/entities/post.entity';
@Entity()
export class Comment {
@PrimaryGeneratedColumn()
id: number;
@Column('text')
text: string;
@CreateDateColumn()
createdAt: Date;
@ManyToOne(() => Post, (post) => post.comments, { onDelete: 'CASCADE' })
post: Post;
}@ManyToOne() means "many comments to one post". The first argument names the related entity, the second the inverse property on it. onDelete: 'CASCADE' deletes the comments together with the post. The comment table gets a postId column.
The inverse side in the post
// src/posts/entities/post.entity.ts
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
import { Comment } from '../../comments/entities/comment.entity';
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 120 })
title: string;
@OneToMany(() => Comment, (comment) => comment.post)
comments: Comment[];
}The other post fields stay the same. @OneToMany() creates no columns — it lets you load comments from a post.
The comments service and controller
Register the entity in the module: imports: [TypeOrmModule.forFeature([Comment]), PostsModule], and add exports: [PostsService] to PostsModule.
// src/comments/comments.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { PostsService } from '../posts/posts.service';
import { Comment } from './entities/comment.entity';
@Injectable()
export class CommentsService {
constructor(
@InjectRepository(Comment)
private readonly commentsRepository: Repository<Comment>,
private readonly postsService: PostsService,
) {}
async create(postId: number, text: string): Promise<Comment> {
const post = await this.postsService.findOne(postId);
const comment = this.commentsRepository.create({ text, post });
return this.commentsRepository.save(comment);
}
}postsService.findOne() throws 404 if the post does not exist, and otherwise returns it — the post object is assigned to the post property.
// src/comments/comments.controller.ts
import { Body, Controller, Param, ParseIntPipe, Post } from '@nestjs/common';
import { CommentsService } from './comments.service';
import { CreateCommentDto } from './dto/create-comment.dto';
@Controller('posts/:postId/comments')
export class CommentsController {
constructor(private readonly commentsService: CommentsService) {}
@Post()
create(@Param('postId', ParseIntPipe) postId: number, @Body() dto: CreateCommentDto) {
return this.commentsService.create(postId, dto.text);
}
}CreateCommentDto has one field, text, with the @IsString() and @IsNotEmpty() rules. Send POST /posts/1/comments with the body {"text":"Great post"} — you get the created comment back.
Loading related data
Relations are not loaded by default. You list them in relations:
// src/posts/posts.service.ts
async findOne(id: number): Promise<Post> {
const post = await this.postsRepository.findOne({
where: { id },
relations: { comments: true },
});
if (!post) {
throw new NotFoundException(`Post ${id} not found`);
}
return post;
}Open /posts/1 — the response now contains a comments array.
Common mistake
The code reads post.comments.length, and the server crashes with Cannot read properties of undefined. The reason is that the post was loaded without relations, so the object simply has no comments property. TypeORM never loads relations on its own: if you need them, specify relations in every query where you use them.