🌕

TypeScript + Nestjs + Prisma でGraphQLサーバー構築

2022/02/21に公開

概要

TypeScript + Nestjs + Prisma でGraphQLサーバー構築。
ついでにdocker-composeでmysqlサーバーも構築。

Nestjsとは?

NestJSは、素早いサーバー実装を可能にし、テストしやすく作ることを目指したTypescript製のWebフレームワークです。GraphQLを扱う際は、ApolloServerと一緒に使われることが多い印象。

https://engineering.mercari.com/blog/entry/20210818-mercari-shops-nestjs-graphql-server/
https://techblog.yahoo.co.jp/entry/2020121530052952/

Prismaとは?

3要素から成る、Node.jsとTypeScriptのORM。

  • Type-safe database client: 型安全なORM
  • Hassle-free migrations: 強力なMigration
  • Visual database browser: DBをブラウザで見れたり、操作ができる

Rubyで言うActiveRecordのような確固たる地位を築きつつあるORM。
https://zenn.dev/mizchi/articles/cbe81299e145491676f8
https://speakerdeck.com/qsona/architecture-decision-for-the-next-n-years-at-studysapuri

手順(簡単なUserモデルを作ってみる)

今回実装したコードは、Gitにて公開しています。
https://github.com/hayashikengo/prisma-nest-apollo-api

Nestjs: プロジェクト作成

$ npm install -g @nestjs/cli
$ nest new test-api

実行して、localhost:3000にブラウザでアクセスしてみる。

$ cd test-api
$ npm run start
$ npx prisma init

Hello World!が表示される。

Prism: セットアップ

$ npm install prisma --save-dev
$ npm install @prisma/client
$ npx prisma init

prividerをmysqlに変更。

prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

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

.envを編集。

.env
- DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
+ DATABASE_URL="mysql://root:password@localhost:4406/mydb?schema=public"

mysqlの開発環境を構築

docker-composeでmysql環境を作る。
下記のレポジトリをテンプレートとして作成しています。

https://github.com/hayashikengo/docker-compose-mysql

下記のディレクトリ構成を作っていきます。

$ tree docker -a
docker
├── .gitignore
├── conf
│   └── mysql.cnf
├── data
│   └── .gitkeep
└── mysql.env

必要なディレクトリを作成。

$ mkdir docker
$ mkdir docker/conf
$ mkdir docker/data
$ touch docker/data/.gitkeep

mysql.cnfを作成。

docker/conf/mysql.cnf
[mysqld]

skip-host-cache
skip-name-resolve

server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
binlog_do_db = mydb

.gitignoreを作成。

docker/.gitignore
data

mysql.envを作成。

docker/mysql.env
MYSQL_ROOT_PASSWORD=password
MYSQL_PORT=3306
MYSQL_USER=mydb_user
MYSQL_PASSWORD=mydb_pwd
MYSQL_DATABASE=mydb
MYSQL_LOWER_CASE_TABLE_NAMES=0

docker-compose.yamlを作成。

docker-compose.yaml
version: '3'

networks:
  overlay:

services:
  mydb:
    image: mysql:5.7
    env_file:
      - ./docker/mysql.env
    container_name: "mydb"
    restart: "no"
    ports:
      - 4406:3306
    volumes:
      - ./docker/conf/mysql.cnf:/etc/mysql/conf.d/mysql.conf.cnf
      - ./docker/data:/var/lib/mysql
    networks:
      - overlay
$ docker-compose up --detach

Prisma: model作成

schema.prismaに下記を追加。

prisma/schema.prisma
model User {
  id          Int      @id @default(autoincrement())
  username    String
  displayName String
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
}

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

$ npx prisma migrate dev --name user

prisma studioでデータを入力する。

$ npx prisma studio

NestjsServiceでPrismaClientを使う

src/prisma.service.tsを作成する。

src/prisma.service.ts
import { INestApplication, Injectable, OnModuleInit } 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();
    });
  }
}

この設定はオプショナルだが、未設定だと最初のDBアクセスの際にコネクションを張りに行く。
設定しておくと、サーバー起動時にコネクションを張ってくれる。

The onModuleInit is optional — if you leave it out, Prisma will connect lazily on its first call to the database. We don't bother with onModuleDestroy, since Prisma has its own shutdown hooks where it will destroy the connection. For more info on enableShutdownHooks, please see Issues with enableShutdownHooks

Resolver実装

GraphQLを扱うのに必要なパッケージをインストール。(公式)

$ npm i @nestjs/graphql @nestjs/apollo graphql apollo-server-express

Nest-cliのプラグインを追加。

nest-cli.json
{
  "collection": "@nestjs/schematics",
- "sourceRoot": "src"
+ "sourceRoot": "src",
+ "compilerOptions": {
+   "plugins": ["@nestjs/graphql"]
+ }
}

app.module.tsに3点の設定を加える。

  • driverとしてApolloDriverを設定
  • autoSchemaFileを指定
  • sortSchemaをtrueに
src/app.module.ts
@Module({
  imports: [
    GraphQLModule.forRoot({
      driver: ApolloDriver,
      autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
      sortSchema: true,
    }),
  ],
  controllers: [AppController],
  providers: [AppService, PrismaService, UsersResolver],
})
export class AppModule {}

user.model.tsを作成する。

src/models/user.model.ts
import { Field, ID, ObjectType } from '@nestjs/graphql';

@ObjectType()
export class User {
  @Field(() => ID)
  id: number;
  username: string;
  displayName: string;
  createdAt: Date;
  updatedAt: Date;
}

src/user/user.service.tsを作成する。

src/user/user.service.ts
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { User } from 'src/models/user.model';
import { PrismaService } from 'src/prisma.service';

@Resolver(() => User)
export class UsersResolver {
  constructor(private prisma: PrismaService) {}

  @Query(() => [User])
  async users() {
    return this.prisma.user.findMany();
  }

  @Mutation(() => User)
  async createUser(
    @Args('username') username: string,
    @Args('displayName') displayName: string,
  ) {
    return this.prisma.user.create({ data: { username, displayName } });
  }
}

動かしてみる

$ npm run start

ブラウザで開く。

$ open http://localhost:3000/graphql

リクエストが帰ってくるのを確認。

さいごに

GraphQLサーバー構築をする際は、ぜひともNestjs&Prismaを積極検討してみたいと思いました。
実際に実装するにあたって、参考になりそうなレポジトリがあったのでリンクを張っておきます。
https://github.com/lujakob/nestjs-realworld-example-app
https://github.com/notiz-dev/nestjs-prisma-starter

あと、twitterで開発について情報発信していますので、よかったらフォローしてくださいmm
https://twitter.com/kenbu05

参考

https://docs.nestjs.com/graphql/quick-start
https://docs.nestjs.com/recipes/prisma
https://zenn.dev/rince/articles/50a66241d04f0b#prismaとは

Discussion