Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Связи: статьи и комментарии

Связь (relation) описывает, как строки одной таблицы относятся к строкам другой. У одной статьи может быть много комментариев, а каждый комментарий принадлежит одной статье — это связь «один ко многим». TypeORM задаёт её декораторами @OneToMany() и @ManyToOne() и сам добавляет в таблицу комментариев столбец со ссылкой на статью.

Ресурс комментариев

nest g resource comments

Выберите REST API и ответьте n на вопрос о CRUD-точках: маршруты комментариев будут вложены в адрес статьи, и их удобнее написать вручную.

Сущность комментария

Создайте файл 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() — «много комментариев к одной статье». Первый аргумент называет связанную сущность, второй — обратное свойство в ней. onDelete: 'CASCADE' удаляет комментарии вместе со статьёй. В таблице comment появится столбец postId.

Обратная сторона в статье

// 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[];
}

Остальные поля статьи не изменились. @OneToMany() не создаёт столбцов — он позволяет загрузить комментарии из статьи.

Сервис и контроллер комментариев

Зарегистрируйте сущность в модуле: imports: [TypeOrmModule.forFeature([Comment]), PostsModule], а в PostsModule добавьте exports: [PostsService].

// 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() бросит 404, если статьи нет, а иначе вернёт её — объект статьи присваивается свойству post.

// 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 содержит одно поле text с правилами @IsString() и @IsNotEmpty(). Отправьте POST /posts/1/comments с телом {"text":"Отличная статья"} — придёт созданный комментарий.

Загрузка связанных данных

По умолчанию связи не загружаются. Их перечисляют в 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(`Статья ${id} не найдена`);
  }
  return post;
}

Откройте /posts/1 — в ответе появится массив comments.

Частая ошибка

Код обращается к post.comments.length, а сервер падает с Cannot read properties of undefined. Причина в том, что статью загрузили без relations, и свойства comments у объекта просто нет. TypeORM никогда не подгружает связи сам: если они нужны, указывайте relations в каждом запросе, где к ним обращаетесь.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.