NestJS のチュートリアル、テーブルのリレーションを作る

# UserTable の準備
User Entity を追加し、Book Entity とリレーションを作ります。
まずは、ID と 名前だけをもつUser Entity を準備し、対応するUserTable を作成します。

## UserEntity を作成する
src/entities ディレクトリにuser.entity.ts を作成し、User Entity を設定します。

```ts:src/entities/user.entity.ts
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: string;

  @Column()
  name: string;

  @Column()
  createdAt: string;

  @Column()
  updatedAt: string;
}
```

## マイグレーション の実行
一度アプリを起動し、entity ファイルをコンパイルします。

```zsh
npm run start:dev
```

次に、マイグレーションファイル作成用のコマンドを実行します。

```zsh
npx typeorm migration:generate -n CreateUser
```

src/migrations ディレクトリにマイグレーションファイルが追加されていれば、再度アプリを再起動します。

最後に、マイグレーション用のコマンドを実行します。

```zsh
npx typeorm migration:run
```

UserTable が作成されていれば成功です。
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684930/2182b0c3-a54e-ff02-a159-83fa363f2292.png)

# Userのcreate 機能を作成する

## User 用のresource を準備する

nest g コマンドを実行し、UsersModule、UsersController、UsersService を作成します。

```zsh
nest g module users
nest g controller users --no-spec
nest g service users --no-spec
```

## UserRepository を作成する

src/users ディレクトリにuser.repository.ts を作成し、UserRepository を定義します。

```ts:src/users/user.repository.ts
import { User } from 'src/entities/user.entity';
import { EntityRepository, Repository } from 'typeorm';

@EntityRepository(User)
export class UserRepository extends Repository<User> {}
```

UsersModule のimports プロパティにUserRepository を登録します。

```ts:src/users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserRepository } from './user.repository';

@Module({
  imports: [TypeOrmModule.forFeature([UserRepository])],
  providers: [UsersService],
  controllers: [UsersController],
})
export class UsersModule {}
```
## Create 機能の実装

UserRepository にUser 保存用のcreate() メソッドを記述します。

```ts:src/users/user.repository.ts
import { User } from 'src/entities/user.entity';
import { EntityRepository, Repository } from 'typeorm';
import { CreateUserDto } from './dto/create-user.dto';

@EntityRepository(User)
export class UserRepository extends Repository<User> {
  async createUser(createUserDto: CreateUserDto): Promise<User> {
    const { name } = createUserDto;

    const user = this.create({
      name,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    });
    await this.save(user);
    return user;
  }
}
```

また、UsersService にUserRepository で作成したcreateUser() メソッドを呼び出すためのcreate() メソッドを記述します。

```ts:src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { User } from 'src/entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UserRepository } from './user.repository';

@Injectable()
export class UsersService {
  constructor(private readonly userRepository: UserRepository) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    return await this.userRepository.createUser(createUserDto);
  }
}
```

最後に、UsersService のcreate() メソッドをUsersController で呼び出せるように調整します。

```ts:src/users/users.service.ts
import { Body, Controller, Post } from '@nestjs/common';
import { User } from 'src/entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  async create(@Body() createUserDto: CreateUserDto): Promise<User> {
    return await this.usersService.create(createUserDto);
  }
}
```
# User とBook のリレーション追加

## User Entity の調整

User Entity Book Entity に対して、OneToMany のリレーションを定義します。

```ts:src/entities/user.entity.ts
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
import { Book } from './book.entity';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: string;

  @Column()
  name: string;

  @Column()
  createdAt: string;

  @Column()
  updatedAt: string;

  @OneToMany(() => Book, (book) => book.user)
  books: Book[];
}
```

## Book Entity の調整

Book Entity にはUser Entity に対して、ManyToOne のリレーションを定義します。

また、リレーション関係にあるUser のId を保存するためのColumn も追加します。

```ts:src/entities/book.entity.ts
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { BookStatus } from '../books/book-status.enum';
import { User } from './user.entity';

@Entity()
export class Book {
  @PrimaryGeneratedColumn()
  id: string;

  @Column()
  name: string;

  @Column()
  status: BookStatus;

  @Column()
  createdAt: string;

  @Column()
  updatedAt: string;

  @ManyToOne(() => User, (user) => user.books)
  user: User;

  @Column()
  userId: string;
}
```
## マイグレーション

アプリを機動し直し、マイグレーションファイル作成用のコマンドを実行します。

```bash
npx typeorm migration:generate -n AddRelation
```

src/migrations ディレクトリにマイグレーションファイルが追加されていることを確認し、アプリを機動しなおします。
マイグレーション用のコマンドを実行します。

```bash
npx typeorm migration:run
```

BookTable にuserId が追加されていれば成功です。
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684930/544d1749-e987-d313-f789-da353c342d88.png)
## CreateBookDto の調整

認証機能を利用して、Book Entity 内のuserId に値を渡すこともできますが、今回は簡単に動作確認だけをしたいので、そのままリクエストの中にuserId の値を渡す実装にします。

CreateBookDto にuserId を追加します。

```ts:src/books/dto/create-book.dto.ts
import { BookStatus } from '../book-status.enum';

export class CreateBookDto {
  name: string;
  status: BookStatus;
  userId: string;
}
```

BooksController からにuserId の値を受け取れるようにBookRepository を調整します。

```ts:src/books/book.repository.ts
import { Book } from 'src/entities/book.entity';
import { EntityRepository, Repository } from 'typeorm';
import { BookStatus } from './book-status.enum';
import { CreateBookDto } from './dto/create-book.dto';

@EntityRepository(Book)
export class BookRepository extends Repository<Book> {
  async createBook(createBookDto: CreateBookDto): Promise<Book> {
    //　userId　を追加する
    const { name, userId } = createBookDto;

    const book = this.create({
      name,
      status: BookStatus.RENTABLE,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
      userId,
    });
    await this.save(book);
    return book;
  }
}
```
動作確認として、リクエストボディにuserId の値を渡し、POST リクエストを送ります。
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684930/343addae-342d-f6d6-c9a8-6511c26e7b03.png)
BooksTable のuserId に値が保存されていれば成功です。
![Untitled (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684930/6911ce1d-3414-d33b-78e1-6f40ac35f6f2.png)

# リレーションを使ったBook の取得

最後に、User のID と一致するuserId を持つBook を取得する処理を実装していきます。

## BookRepository の登録

今回は、UsersService の中に検索機能を持たせます。

BookRepository をUsersService 内で使用できるように、UsersModule のimports プロパティに登録します。

```ts:src/users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserRepository } from './user.repository';
import { BookRepository } from 'src/books/book.repository';

@Module({
  imports: [TypeOrmModule.forFeature([UserRepository, BookRepository])],
  providers: [UsersService],
  controllers: [UsersController],
})
export class UsersModule {}
```

UsersService 内でBookRepository をインスタンス化します。

```ts:src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { BookRepository } from 'src/books/book.repository';
import { User } from 'src/entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UserRepository } from './user.repository';

@Injectable()
export class UsersService {
  constructor(
    private readonly userRepository: UserRepository,

　　　　　　　　// BookRepository をインスタンス化する
    private readonly bookRepository: BookRepository,
  ) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    return await this.userRepository.createUser(createUserDto);
  }
}
```

## 検索機能の実装

リクエストで送られてきたUser のID と同じuserId を持つBook を検索する処理をUsersService  に記述します。

```ts:src/users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { BookRepository } from 'src/books/book.repository';
import { Book } from 'src/entities/book.entity';
import { User } from 'src/entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UserRepository } from './user.repository';

@Injectable()
export class UsersService {
  constructor(
    private readonly userRepository: UserRepository,
    private readonly bookRepository: BookRepository,
  ) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    return await this.userRepository.createUser(createUserDto);
  }

  async findOwnedBooks(id: string): Promise<Book[]> {
    const user = await this.userRepository.findOne(id);
    if (!user) {
      throw new NotFoundException();
    }
    const books = await this.bookRepository.find();
    return books.filter((book) => book.userId === user.id);
  }
}
```

UsersController にGET リクエストとUsersService のfindOwnedBooks() メソッドを紐付ける処理を記述ます。
リクエストのURL はusers/owned/:id としました。

```ts:src/users/users.controller.ts
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { Book } from 'src/entities/book.entity';
import { User } from 'src/entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  async create(@Body() createUserDto: CreateUserDto): Promise<User> {
    return await this.usersService.create(createUserDto);
  }

  @Get('/owned/:id')
  async findOwnedBooks(@Param('id') id: string): Promise<Book[]> {
    return await this.usersService.findOwnedBooks(id);
  }
}
```

http://localhost:3000/users/owned/:id のパスにGet リクエストを送ってみて、渡したID と同じ値のuserId を持つBook の配列が取れれば完成です。
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684930/54bd2534-6355-0637-c4ae-5cc53069671b.png)