Oteto Blogのロゴ

NestJS(Fastify) × TypeORM × PostgreSQL × Dockerで環境構築

タイトルの構成で、マイグレーションが実行できる一歩手前までの環境構築を行う。

環境構築手順

1. Dockerの設定

mkdir <PROJECT_NAME>
touch Dockerfile docker-compose.yml .dockerignore

1-1. Dockerfile

FROM node:18.14.1-alpine

WORKDIR /app

RUN npm i -g @nestjs/cli

1-2. docker-compose.yml

version: '3.8'
services:
  app:
    container_name: app
    build: .
    tty: true
    stdin_open: true
    volumes:
      - .:/app
      - /app/node_modules
    depends_on:
      - db
    environment:
      TZ: Asia/Tokyo
    ports:
      - '3000:3000'

  db:
    container_name: db
    image: postgres:14.2-alpine
    restart: always
    volumes:
      - ./docker/postgres/init.d:/docker-entrypoint-initdb.d
      - ./docker/postgres/pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pw
      TZ: 'Asia/Tokyo'
    ports:
      - '5432:5432'

  db-gui:
    container_name: db-gui
    image: dpage/pgadmin4
    volumes:
      - ./docker/pgadmin:/var/lib/pgadmin
    depends_on:
      - db
    environment:
      PGADMIN_DEFAULT_EMAIL: email@email.com
      PGADMIN_DEFAULT_PASSWORD: pw
    ports:
      - '80:80'

今回はWeb GUI上でDBの操作を行いたいのでpgadmin4も導入しておく。

1-3 .dockerignore

node_modules

1-4. ビルド

docker compose up -d --build
docker compose ps

上記コマンドを叩きコンテナが起動していればOK。

もしMac・Docker Desktopを利用していてビルドが失敗する際は下記の方法で解決するかも。

【Docker】ビルドが.docker/buildx/current:permission deniedとエラーになる

2. コンテナに入る

docker exec -it app sh

以後はコンテナ内で作業していく。

3. Nestプロジェクトの作成

nest new .
? Which package manager would you ❤️  to use? (Use arrow keys)
❯ npm 
  yarn 
  pnpm 

と聞かれるのでnpmを選択。

npm run start:dev

localhost:3000にアクセスしてHello World!が表示されればアプリ側はOK。

FROM node:18.14.1-alpine

WORKDIR /app

RUN npm i -g @nestjs/cli
COPY ./package.json .
COPY ./package-lock.json .
RUN npm install

コンテナを立ち上げる際に各種npmパッケージのインストールを行いたいので、Dockerfileに追記しておく。

4. Fastifyに切り替え

npm i @nestjs/platform-fastify
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import {
  FastifyAdapter,
  type NestFastifyApplication,
} from '@nestjs/platform-fastify';

async function bootstrap() {
  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();

main.tsを書き換える。

5. pgAdminからDBに接続する

下記に沿ってpgAdminにログインし、PostgresのDBに接続できることを確認する。

【Docker】pgAdmin4でPostgreSQLに接続&永続化する

6. DBの接続情報

6-1. 環境変数に設定

touch .env
DATABASE_HOST=db
DATABASE_NAME=mydb
DATABASE_USER=user
DATABASE_PASSWORD=pw
DATABASE_PORT=5432

6-2. ConfigModuleの導入

npm i @nestjs/config
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  imports: [ConfigModule.forRoot({ isGlobal: true })],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

.envの値を参照するためにconfigServiceをDIする。

7. TypeORMの導入

npm i @nestjs/typeorm typeorm pg
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigService } from '@nestjs/config';

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        type: 'postgres',
        host: configService.get<string>('DATABASE_HOST'),
        database: configService.get<string>('DATABASE_NAME'),
        username: configService.get<string>('DATABASE_USER'),
        password: configService.get<string>('DATABASE_PASSWORD'),
        port: Number(configService.get<string>('DATABASE_PORT')),
        entities: [],
        synchronize: false,
      }),
    }),
  ],
})
export class DatabaseModule {}

TypeORMの設定専用のモジュールsrc/database/database.module.tsを作成し、上記のようにConfigServiceをDIしてDBへの接続オプションを設定する。

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { DatabaseModule } from './database/database.module';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [ConfigModule.forRoot({ isGlobal: true })],
  imports: [ConfigModule.forRoot({ isGlobal: true }), DatabaseModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

あとはapp.module.tsでimportすれば設定完了。

[9:18:11 PM] File change detected. Starting incremental compilation...

[9:18:11 PM] Found 0 errors. Watching for file changes.

[Nest] 433  - 09/29/2023, 9:18:12 PM     LOG [NestFactory] Starting Nest application...
[Nest] 433  - 09/29/2023, 9:18:12 PM     LOG [InstanceLoader] DatabaseModule dependencies initialized +9ms
[Nest] 433  - 09/29/2023, 9:18:12 PM     LOG [InstanceLoader] TypeOrmModule dependencies initialized +0ms

npm run start:devしてエラーが吐かれていないことを確認。

次のステップ

NestJS × TypeORM 0.3 でCLIからmigrationする