NestJS + MySQL + Prisma + GraphQL環境の構築

タイトルの組み合わせで、GraphQLのデータ取得Queryが実行可能なところまで構築する。
なお、GraphQLの実装はコードファーストとスキーマファーストの2種類があるが、今回はコードファーストで実装する。

※Prismaはライブラリの更新速度が早く、コマンドが陳腐化している可能性があるため注意
コマンド実行後のエラー等に最新verでの実行方法の補足が表示されるため、エラーが出た場合はそちらを参考にする

Init Nest

パッケージ追加とプロジェクトの初期化を行う。

npm i -g @nestjs/cli

nest new app-name

Init MySQL

MySQLを起動するdocker-compose.ymlを用意する。

※開発用途で脆弱な設定であるため注意。

version: '3.8'
services:
  db:
    image: mysql:8
    container_name: db
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - '3306:3306'
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - ./mysql:/var/lib/mysql

MySQLを起動する。

docker-compose up

Init Prisma

Prismaを設定する。

yarn add --dev prisma

npx prisma init

生成されたschema.prismaproviderをMySQLに変更。

- provider = "postgresql"
+ provider = "mysql"

また、データベースの初期化用にIDだけのユーザーモデルを追記する。

model User {
  id String @id @default(uuid())
}

.envに接続情報を定義

# Database
DATABASE_URL="mysql://root:password@localhost/db"

マイグレーションを実行する。

npx prisma migrate dev

nestjsでPrismaのServiceを用意する。

nest g module prisma
nest g service prisma

作成したprisma.service.tsを下記のように修正。

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

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

  async onModuleDestroy() {
    await this.$disconnect();
  }
}

prisma.module.tsPrismaServiceをエクスポートする。

import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Module({
  providers: [PrismaService],
  exports: [PrismaService],
})
export class PrismaModule {}

GraphQL設定

必要なライブラリをインストールする。

yarn add @nestjs/graphql graphql-tools graphql apollo-server-express

app.module.tsにGraphQLModuleのインポート設定を追記。

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { Request, Response } from 'express';
import { join } from 'path';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PrismaModule } from './prisma/prisma.module';

@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
      // Resolverでexpressのreq/resを利用する場合設定する
      // context: ({ req, res }): { req: Request; res: Response } => ({
      //   req,
      //   res,
      // }),
      // corsの設定が必要な場合
      // cors: {
      //   origin: process.env.ORIGINS?.split(','),
      //   credentials: true,
      // },
      debug: process.env.NODE_ENV === 'production' ? false : true,
      playground: process.env.NODE_ENV === 'production' ? false : true,
    }),
    PrismaModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

コードファーストの定義を簡略化するため、nest-cli.jsonに下記設定を追記。

{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "plugins": [
      {
        "name": "@nestjs/graphql/plugin",
        "options": {
          "typeFileNameSuffix": [
            ".input.ts",
            ".args.ts",
            ".entity.ts",
            ".model.ts"
          ]
        }
      }
    ]
  }
}

GraphQL実装

Userモデルのデータを取得できるリゾルバを作成する。

nest g mo Users
nest g resolver Users
nest g service Users

users.module.tsimportsPrismaModuleを追記。

+   imports: [PrismaModule]

entityを定義する。

touch src/users/user.entity.ts
import { ObjectType } from '@nestjs/graphql';

@ObjectType()
export class User {
  id!: string;
}

users.service.tsにPrismaを使用してユーザーを全件取得するメソッドを定義する。

import { Injectable } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';

@Injectable()
export class UsersService {
  constructor(private readonly prisma: PrismaService) {}

  findAll() {
    return this.prisma.user.findMany();
  }
}

users.resolver.tsにユーザーを全件取得するリゾルバを定義する。

import { Query, Resolver } from '@nestjs/graphql';
import { User } from './user.entity';
import { UsersService } from './users.service';

@Resolver()
export class UsersResolver {
  constructor(private readonly usersService: UsersService) {}

  @Query(() => [User], { name: 'users' })
  findAll(): Promise<User[]> {
    return this.usersService.findAll();
  }
}

app.module.tsUsersModule のimportを追加する。

...
  imports: [
    ...
    ProfilesModule,
  ],
...

Prisma Studioを用いて、Webインターフェースから適当にUserを追加する。

npx prisma studio
# デフォルトでhttp://localhost:5555でPrisma Studioが起動するので、ユーザーテーブル選択後、Add recordからデータを追加する

接続確認

実際にアプリケーションを起動し、GraphQL Playgroundからユーザーデータを取得する。

yarn start:dev

アプリケーションが起動したらhttp://localhost:3000/graphqlにアクセスし、下記クエリを実行する。

query {
  users {
    id
  }
}

データが正常に取得できればセットアップ完了。

{
  "data": {
    "users": [
      {
        "id": "4d3147b8-0b2d-4c6b-a54d-b9bbb2d627b5"
      },
      {
        "id": "590ee871-e7ba-42d0-be0c-ce55377112cf"
      },
      {
        "id": "9177215b-b055-4140-84bf-e89233d1ef49"
      }
    ]
  }
}

References