NestJSでREST API
環境構築
Node.jsが利用できる環境であれば特に環境は指定しませんが, Dockerの場合は下記のような環境を使用します。
docker run -u node -it -w /home/node/workspace -v $HOME/nestjs:/home/node/workspace node sh -c "yarn global add @nestjs/cli && /home/node/.yarn/bin/nest new ."
ここまででグローバル環境に@nestjs/cli
をインストールし, プロジェクトのルートでnest new .
を実行します。途中, 使用するパッケージマネージャを聞かれるので適当なものを選択してください。
Fastifyを使う
NestJSはデフォルトではExpressを使用しますが, パフォーマンスの面からFastifyへ変更していきます。
yarn add @nestjs/platform-fastify
インストール後はsrc/main.ts
を変更します。
// 省略
- const app = await NestFactory.create(AppModule);
+ const app = await NestFactory.create<NestFastifyApplication>(
+ AppModule,
+ new FastifyAdapter(),
+ );
// 省略
- await app.listen(3000);
+ await app.listen(3000, '0.0.0.0');
}
bootstrap();
Prismaを使う
yarn add --dev prisma
yarn prisma init
Prismaのプロジェクトを初期化すると.env
とprisma/schema.prisma
が生成されるため適宜編集していきます。DATABASE_URL
はPrisma公式で扱われている書式で使用することができます。prisma/schema.prisma
ではデータベースのテーブルを記述することができます。ここでは簡単にUser
テーブルをひとつ定義することにします。
// 省略
+ model User {
+ id Int @id @default(autoincrement())
+ username String
+ }
}
スキーマの定義の後は初回マイグレーションを実行します。
yarn prisma generate
yarn prisma migrate dev --name init
yarn prisma generate
のみでもTypeScriptの型を使用することができます。
NestJSのリソースを追加する
NestJSでは簡単にCRUDを含むResourcesを生成することができます。
nest generate resource user
上記はnest g res user
をエイリアスとして使用することができます。実行するとuser
について, REST APIやGraphQLといったレイヤを選択することができます。REST APIを選択すると次のファイルが生成されます。
- DTO(Data Transfer Object)
- Controller
- Service
- Entity
API仕様書を作る
Fastifyを使用する場合は@fastify/static
も同時にインストールします。
yarn add @nestjs/swagger @fastify/static
main.ts
を次のように変更すると, OpenAPIのドキュメントを使用することができます。
// 省略
+ const config = new DocumentBuilder()
+ .setTitle('User example')
+ .setDescription('The users API description')
+ .setVersion('1.0')
+ .build();
+ const document = SwaggerModule.createDocument(app, config);
+ SwaggerModule.setup('api', app, document);
await app.listen(3000, '0.0.0.0');
}
bootstrap();
yarn start
でサーバを起動しlocalhost:3000/api
を開くとGUIからAPIを使用することができます。同時に, api-yaml
とapi-json
でそれぞれyamlとjsonを取得することができます。
ドキュメントを作る
デコレータを用いてOpenAPIでの例や型を付けていきます。
// 省略
+ export class CreateUserDto {
+ @ApiProperty({ example: 'john Doe' })
+ username: string;
+ }
DTOとEntityは上記のように変更することで注釈と型をSwagger UIから確認することができます。
// 省略
+ @ApiTags('user')
@Controller('user')
export class UserController {
// 省略
また, ControllerにApiTags
デコレータを使用することでController全体をuser
としてまとめることができます。
DI(Dependency Injection)
データベースを扱うクラスを抽象化し, テスト可能なServiceを作ります。はじめに次のファイルを作成します。
import { User } from 'src/user/entities/user.entity';
export abstract class UsersRepository {
abstract create(user: User): Promise<User>;
abstract readAll(): Promise<User[]>;
abstract readById(id: number): Promise<User>;
abstract update(user: User): Promise<User>;
abstract delete(id: number): Promise<User>;
}
export class UserRepositoryError extends Error {
readonly type: UserRepositoryErrorType;
constructor(type: UserRepositoryErrorType) {
super();
this.type = type;
}
}
export type UserRepositoryErrorType = 'RECORD_NOT_FOUND' | 'FATAL';
抽象クラスについて注意すべき点はDIのためにinterface
ではなくabstract class
を使用します。実装は次のようになります。
import { User } from 'src/user/entities/user.entity';
import { UserRepositoryError, UsersRepository } from './users.repository';
import { PrismaClient } from '@prisma/client';
export class UsersDb extends UsersRepository {
async create(user: User): Promise<User> {
try {
const prisma = new PrismaClient();
return prisma.user.create({
data: { id: user.id, username: user.username },
});
} catch (error) {
throw new UserRepositoryError('FATAL');
}
}
// 省略
}
次に, src/user/user.module.ts
でDIを実装します。
// 省略
@Module({
controllers: [UserController],
- providers: [UserService]
+ providers: [UserService, { provide: UsersRepository, useClass: UsersDb }],
})
export class UserModule {}
src/user/user.service.ts
では次のように抽象クラスを使用することができます。
@Injectable()
export class UserService {
+ constructor(
+ @Inject(UsersRepository) private readonly usersRepository: UsersRepository,
+ ) {}
- create(createUserDto: CreateUserDto) {
+ async create(createUserDto: CreateUserDto): Promise<User> {
// 省略
}
参考文献
- NestJS Docs. https://docs.nestjs.com/
- “Performance (Fastify)”. https://docs.nestjs.com/techniques/performance
- “OpenAPI (Swagger)”. https://docs.nestjs.com/openapi/introduction
- Prisma Documantation. https://www.prisma.io/docs
- “Connection URLs”. https://www.prisma.io/docs/reference/database-reference/connection-urls
Discussion