NestJSに入門します。
プロジェクトを新しく作成する
> npm i -g @nestjs/cli
> nest new nestjs-trial
⚡ We will scaffold your app in a few seconds..
? Which package manager would you ❤️ to use? npm
CREATE nestjs-trial/.eslintrc.js (663 bytes)
CREATE nestjs-trial/.prettierrc (51 bytes)
CREATE nestjs-trial/README.md (3340 bytes)
CREATE nestjs-trial/nest-cli.json (171 bytes)
CREATE nestjs-trial/package.json (1945 bytes)
CREATE nestjs-trial/tsconfig.build.json (97 bytes)
CREATE nestjs-trial/tsconfig.json (546 bytes)
CREATE nestjs-trial/src/app.controller.ts (274 bytes)
CREATE nestjs-trial/src/app.module.ts (249 bytes)
CREATE nestjs-trial/src/app.service.ts (142 bytes)
CREATE nestjs-trial/src/main.ts (208 bytes)
CREATE nestjs-trial/src/app.controller.spec.ts (617 bytes)
CREATE nestjs-trial/test/jest-e2e.json (183 bytes)
CREATE nestjs-trial/test/app.e2e-spec.ts (630 bytes)
✔ Installation in progress... ☕
🚀 Successfully created project test
👉 Get started with the following commands:
$ cd test
$ npm run start
Thanks for installing Nest 🙏
Please consider donating to our open collective
to help us maintain this package.
🍷 Donate: https://opencollective.com/nest
ディレクトリの状態
> cd nestjs-trial
> tree -I node_modules
.
├── README.md
├── nest-cli.json
├── package-lock.json
├── package.json
├── src
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ └── main.ts
├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── tsconfig.build.json
└── tsconfig.json
これで、/
へアクセスするとHello World!
が表示されます。
まずはControllerを作ってみる
> nest g controller cats
CREATE src/cats/cats.controller.spec.ts (478 bytes)
CREATE src/cats/cats.controller.ts (97 bytes)
UPDATE src/app.module.ts (322 bytes)
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats'
}
}
これで、/cats
へアクセスするとThis action returns all cats
が表示されます。
新しいリソースを作ってみる
> nest g resource dogs
? What transport layer do you use? REST API
? Would you like to generate CRUD entry points? Yes
CREATE src/dogs/dogs.controller.spec.ts (556 bytes)
CREATE src/dogs/dogs.controller.ts (873 bytes)
CREATE src/dogs/dogs.module.ts (241 bytes)
CREATE src/dogs/dogs.service.spec.ts (446 bytes)
CREATE src/dogs/dogs.service.ts (595 bytes)
CREATE src/dogs/dto/create-dog.dto.ts (29 bytes)
CREATE src/dogs/dto/update-dog.dto.ts (165 bytes)
CREATE src/dogs/entities/dog.entity.ts (20 bytes)
UPDATE package.json (1978 bytes)
UPDATE src/app.module.ts (381 bytes)
✔ Packages installed successfully.
ディレクトリの状態
> tree -I node_modules
.
├── README.md
├── nest-cli.json
├── package-lock.json
├── package.json
├── src
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ ├── cats
│ │ ├── cats.controller.spec.ts
│ │ └── cats.controller.ts
│ ├── dogs
│ │ ├── dogs.controller.spec.ts
│ │ ├── dogs.controller.ts
│ │ ├── dogs.module.ts
│ │ ├── dogs.service.spec.ts
│ │ ├── dogs.service.ts
│ │ ├── dto
│ │ │ ├── create-dog.dto.ts
│ │ │ └── update-dog.dto.ts
│ │ └── entities
│ │ └── dog.entity.ts
│ └── main.ts
├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── tsconfig.build.json
└── tsconfig.json
DBへ接続してみる
prismaを使ってみます。
> npm i prisma --save-dev
> npx prisma init
✔ Your Prisma schema was created at prisma/schema.prisma
You can now open it in your favorite editor.
warn You already have a .gitignore file. Don't forget to add `.env` in it to not commit any private information.
Next steps:
1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started
2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql, sqlite, sqlserver, mongodb or cockroachdb.
3. Run prisma db pull to turn your database schema into a Prisma schema.
4. Run prisma generate to generate the Prisma Client. You can then start querying your database.
More information in our documentation:
https://pris.ly/d/getting-started
dockerでpostgresqlを立ち上げてそこに接続してみます。
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: postgres
TZ: 'Asia/Tokyo'
ports:
- 5432:5432
volumes:
- ./postgres/data:/var/lib/postgresql/data
下記を実行します。
docker compose up
DBが立ち上がります。
prismaにDogモデルを定義してみます。
環境変数にDATABASE_URLも定義します。
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Dog {
id String @id @default(uuid())
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
DATABASE_URL="postgresql://postgres:password@localhost:5432/postgres"
下記を実行しマイグレーションファイルの作成、DBに対して実行します。
> npx prisma migrate dev --name init
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": PostgreSQL database "postgres", schema "public" at "localhost:5432"
Applying migration `20231220071137_init`
The following migration(s) have been created and applied from new schema changes:
migrations/
└─ 20231220071137_init/
└─ migration.sql
Your database is now in sync with your schema.
Running generate... (Use --skip-generate to skip the generators)
added 1 package, and audited 730 packages in 3s
130 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
✔ Generated Prisma Client (v5.7.1) to ./node_modules/@prisma/client in 45ms
prismaディレクトリの状態
> tree prisma
prisma
├── migrations
│ ├── 20231220071137_init
│ │ └── migration.sql
│ └── migration_lock.toml
└── schema.prisma
PrismaClientを作成しDBへ接続します。
npm i @prisma/client
import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
}
dogsをDBから取得します。
import { Injectable } from '@nestjs/common';
import { CreateDogDto } from './dto/create-dog.dto';
import { UpdateDogDto } from './dto/update-dog.dto';
import { PrismaService } from './../prisma.service'
import { Dog, Prisma } from '@prisma/client';
@Injectable()
export class DogsService {
constructor(private prisma: PrismaService) {}
createDog(createDogDto: CreateDogDto) {
return 'This action adds a new dog';
}
async dogs(params: {
skip?: number;
take?: number;
cursor?: Prisma.DogWhereUniqueInput;
where?: Prisma.DogWhereInput;
orderBy?: Prisma.DogOrderByWithRelationInput;
}): Promise<Dog[]> {
const { skip, take, cursor, where, orderBy } = params;
return this.prisma.dog.findMany({
skip,
take,
cursor,
where,
orderBy,
});
}
dog(id: number) {
return `This action returns a #${id} dog`;
}
updateDog(id: number, updateDogDto: UpdateDogDto) {
return `This action updates a #${id} dog`;
}
deleteDog(id: number) {
return `This action removes a #${id} dog`;
}
}
OpenAPI使ってみる
npm i --save @nestjs/swagger
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('nestjs-trial')
.setDescription('This is NestJS trial.')
.setVersion('1.0.0')
.addTag('trial')
.build()
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document)
await app.listen(3000);
}
bootstrap();
localhost:3000/api
で下記が見れるようになりました。
http://localhost:3000/api-json
にアクセスするとjson形式、http://localhost:3000/api-yaml
にアクセスするとyaml形式で取得できました。
下記のようにすると
import { Controller, Get } from '@nestjs/common';
+ import { ApiTags } from '@nestjs/swagger';
@Controller('cats')
+ @ApiTags('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats'
}
}
こんな感じでタグが表示されます。
他にもいろいろデコレータがあります。
https://docs.nestjs.com/openapi/decorators
バリデーションをしてみる
npm i --save class-validator class-transformer
import { NestFactory } from '@nestjs/core';
+ import { ValidationPipe } from '@nestjs/common'
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('nestjs-trial')
.setDescription('This is NestJS trial.')
.setVersion('1.0.0')
.addTag('trial')
.build()
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document)
+ app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
dogの作成のエンドポイントも作ります。
createDog(data: Prisma.DogCreateInput): Promise<Dog>
return this.prisma.dog.create({
data
});
}
import { IsString } from 'class-validator';
export class CreateDogDto {
@IsString
name: string;
}
例えば、下記のデータをリクエストすると、
{
"name": 1
}
下記のエラーが返ってきました。
{
"message": [
"name must be a string"
],
"error": "Bad Request",
"statusCode": 400
}
Render.comにデプロイしてみる
環境変数にNODE_ENV=production
を追加します。
ビルド時にnest: not found
というエラーになったので、
@nestjs/cli
をdependenciesへ移しました。
// build command
> npm ci && npm run build
// start command
> npm run start:prod
DBも作成してそこに繋ぎます。
今回はSupabaseを使ってみました。
Settings
>Database
>Connection string
のNode.js
タブにある文字列を環境変数のDATABASE_URL
に設定します。
GET /dogs
すると、データが取れました。
[
{
id: "8cb9e5f3-2444-456e-9998-b8dcef88d739",
name: "shiba inu",
createdAt: "2023-12-22T23:38:14.594Z",
updatedAt: "2023-12-22T23:38:14.594Z"
}
]
※リポジトリは下記です。
最後に
私の勤めている株式会社mofmofでNestJSとOpenAIを使ったプロダクト開発を行ったので、あらためてNestJSに入門してみました。
ちなみに上記の企画では観光地をおすすめしてくれるサービスをチームで作りました。
あと、今回の話とは関係ないのですが、このNestJSの入門ではいつも使っているVSCodeとSourcetreeを使わずにNeovimとLazygitを使いました。
慣れてないのでとても辛かったです。
思考のスピードでコーディングできるようになりたい。
株式会社mofmofは受託開発会社ですが、メンバー自らがオーナーとなってプロダクト開発にチャレンジしています。
この記事は、NestJS, 生成AIにチャレンジする「金曜日のチーム開発」の成果です。