😇

GraphQL, Fastify, Nest, Prisma, MySQL, Docker環境をサクッと構築する

2021/07/06に公開

GraphQLが使えてPrismaが使えてDockerなバックエンド開発環境が欲しい!

TL;DR

GraphQL+node+RDBなバックエンド作成時にサクッと作り始められるテンプレートが欲しかったので作った。
最近TypeORMよりPrismaの方がいい感じっぽいのでPrismaもつっこんだ。

https://github.com/nori-k/gql-nest-prisma


とりあえずnodeのバージョンを確認して @nestjs/cli をインストール

参考
https://docs.nestjs.com/first-steps

Terminal.app
node -v             
v16.4.1
❯ npm -v              
7.18.1
~~~~~~~~~~

❯ npm i -g @nestjs/cli 

❯ nest new gql-nest-prisma 
⚡  We will scaffold your app in a few seconds..

CREATE gql-nest-prisma/.eslintrc.js (631 bytes)
CREATE gql-nest-prisma/.prettierrc (51 bytes)
CREATE gql-nest-prisma/README.md (3339 bytes)
CREATE gql-nest-prisma/nest-cli.json (64 bytes)
CREATE gql-nest-prisma/package.json (1977 bytes)
CREATE gql-nest-prisma/tsconfig.build.json (97 bytes)
CREATE gql-nest-prisma/tsconfig.json (339 bytes)
CREATE gql-nest-prisma/src/app.controller.spec.ts (617 bytes)
CREATE gql-nest-prisma/src/app.controller.ts (274 bytes)
CREATE gql-nest-prisma/src/app.module.ts (249 bytes)
CREATE gql-nest-prisma/src/app.service.ts (142 bytes)
CREATE gql-nest-prisma/src/main.ts (208 bytes)
CREATE gql-nest-prisma/test/app.e2e-spec.ts (630 bytes)
CREATE gql-nest-prisma/test/jest-e2e.json (183 bytes)

? Which package manager would you ❤️  to use? npm
✔ Installation in progress... ☕

🚀  Successfully created project gql-nest-prisma
👉  Get started with the following commands:

$ cd gql-nest-prisma
$ 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
                                         

サクッとExpress → Fastifyに変更

参考
https://docs.nestjs.com/techniques/performance

Terminal.app
npm i --save @nestjs/platform-fastify

/src/main.ts を編集してFastifyを使うように設定

main.ts

 import { NestFactory } from '@nestjs/core';
+import {
+  FastifyAdapter,
+  NestFastifyApplication,
+} from '@nestjs/platform-fastify';
 import { AppModule } from './app.module';
 
 async function bootstrap() {
-  const app = await NestFactory.create(AppModule);
+  const app = await NestFactory.create<NestFastifyApplication>(
+    AppModule,
+    new FastifyAdapter(),
+  );
   await app.listen(3000);
 }
 bootstrap();

new FastifyAdapter() は任意で new FastifyAdapter({ logger: true }) にしておくとログが出るので、 process.env.NODE_ENV とかでロガーの設定とかを切り分けると良いのではないでしょうか。


サクサクGraphQLを設定

参考
https://docs.nestjs.com/graphql/quick-start

Terminal.app
npm i @nestjs/graphql graphql-tools graphql apollo-server-fastify
src/app.module.ts

 import { Module } from '@nestjs/common';
+import { GraphQLModule } from '@nestjs/graphql';
 import { AppController } from './app.controller';
 import { AppService } from './app.service';
 
 @Module({
-  imports: [],
+  imports: [GraphQLModule.forRoot({})],
   controllers: [AppController],
   providers: [AppService],
 })

sample.ts
GraphQLModule.forRoot({
  debug: true,
  playground: true,
})

プロジェクトの内容や好みに合わせてGraphQLスキーマの生成の設定をする

  • コードファーストでGQLのスキーマを自動生成する

参考
https://docs.nestjs.com/graphql/quick-start#code-first

  • スキーマファーストで型定義を自動生成する

参考
https://docs.nestjs.com/graphql/quick-start#schema-first


PrismaをORMとして使うのでサクサク設定していく

Prismaのインストールを行う

参考
https://docs.nestjs.com/recipes/prisma#set-up-prisma

Terminal.app
npm install prisma --save-dev

Prismaの初期化をする

Terminal.app
❯ npx prisma init

✔ Your Prisma schema was created at prisma/schema.prisma
  You can now open it in your favorite editor.

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, sqlserver or sqlite.
3. Run prisma db pull to turn your database schema into a Prisma data model.
4. Run prisma generate to install Prisma Client. You can then start querying your database.

More information in our documentation:
https://pris.ly/d/getting-started

Prismaの設定をする

MySQLを使うので npx prisma init で変更された .env と生成された prisma/schema.prisma を編集する

datasource db {
-  privider = "postgresql"
+  provider = "mysql"
  url      = env("DATABASE_URL")
}

DATABASE_URL を任意のDB接続設定に変更する

- DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
+ DATABASE_URL="mysql://root:@localhost:3306/mydb"

Nest, Prismaの設定に合わせて Dockerfiledocker-compose.yml を作成する

Dockerfiledocker-compose.yml ファイルを作成

Terminal.app
touch Dockerfile docker-compose.yml

アプリ用のnodeコンテナの設定を Dockerfile に記述

Dockerfile
# ローカルと合わせておく
FROM node:16.4.1-alpine

# 任意のtime zone設定にする
ENV TZ=Asia/Tokyo
RUN apk --no-cache add tzdata && \
    ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

WORKDIR /app

COPY package.json .
COPY package-lock.json .

RUN npm ci

EXPOSE 3000

ENTRYPOINT [ "npm", "run", "start:dev" ]

docker-compose.yml でDBとアプリコンテナの設定

docker-compose.yml
version: '3.7'
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: gql-nest-prisma #任意のコンテナ名
    tty: true
    stdin_open: true
    volumes:
      - .:/app
      - /app/node_modules
    depends_on:
      - mydb
    ports:
      - '3000:3000'

  mydb:
    image: mysql:5.7
    platform: linuxx86_64 # M1 Macだとこれ設定しないとちゃんと動かない
    container_name: mysqldb
    tty: true
    stdin_open: true
    volumes:
      - ./db/dev:/docker-entrypoint-initdb.d
      - ./db/my.cnf:/etc/mysql/conf.d/my.cnf
    environment:
      MYSQL_DATABASE: mydb
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
      MYSQL_USER: user
      MYSQL_PASSWORD: password # 任意のPWを設定する
      TZ: Asia/Tokyo
      # MYSQL_ROOT_PASSWORDは今回設定していないが必要に応じて設定する。
      # .env の接続情報も編集すること
    ports:
      - '3306:3306'


Prisma Clientを導入する

参考
https://docs.nestjs.com/recipes/prisma#install-and-generate-prisma-client

Terminal.app
❯ npm install @prisma/client

ここ( https://docs.nestjs.com/recipes/prisma#install-and-generate-prisma-client )を参考に prisma.service.ts と main.ts を設定する

prisma.service.ts
import {
  INestApplication,
  Injectable,
  OnModuleInit,
  OnModuleDestroy,
} from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
  }

  async enableShutdownHooks(app: INestApplication) {
    this.$on('beforeExit', async () => {
      await app.close();
    });
  }
}

参考
https://docs.nestjs.com/recipes/prisma#issues-with-enableshutdownhooks

main.ts
....
import {
   FastifyAdapter,
   NestFastifyApplication,
 } from '@nestjs/platform-fastify';
+import { PrismaService } from './prisma.service';
 import { AppModule } from './app.module';
 
 async function bootstrap() {
.... 
async function bootstrap() {
     AppModule,
     new FastifyAdapter(),
   );
+  const prismaService: PrismaService = app.get(PrismaService);
+  prismaService.enableShutdownHooks(app);
   await app.listen(3000);
 }
 bootstrap();

Let's Developing!

以上で環境の設定は終わりました!

docker-compose up すれば開発サーバー&DBが立ち上がるかと思います。
ここから schema.prisma にテーブル定義を構築したり、各種モジュールを作ったり、
GraphQLのスキーマを作ったりしていきましょう!!
また、バリデーションや認証設定したり、外部サービスを呼び出す場合はそれを作る必要もあります。
テストも書かないといけないでしょう。
NestJSのCLIやNestJSに最初から用意されている機能は非常に強力なので、それらを使えばサクサクと開発を進めていけるはずです。

nodeバックエンドなんもわからん。 の自分でもサクッと環境が作れてバックエンドの開発に集中できる環境が作れました。
dockerに乗ってるのでプロダクションリリースも楽ちんです。

実際にプロダクト開発で使う際には
ドキュメント生成や、
huskyを使った整形、テストなどの自動化を設定したりしても開発体験をガンガン上げていけると思います。
手元とコンテナ内のnodeのバージョンを合わせるために asdf.tool-versions を設定したりしてたりもします。

時間が取れたら続きとして実際にGraphQLのAPIを立ててPlaygroundでCRUDしてみた記事を投稿するかもしれません。


参考にしたドキュメントなど
nestjs公式ドキュメント
prisma.io/nestjs
prismaのnestjs-graphqlリポジトリ
fivethree-team/nestjs-prisma-starter
NestJS導入 husky編

Discussion