NestJS + graphQL + Prisma + Firebase でToDOリストを作ろう!〜セットアップ編〜
はじめに
NestJS + Prisma + graphql + Firebaseを利用してToDoリスト用のAPIサーバーを構築します!
今回はNestJSにORMとgraphqlを組み込むところまでやっていきます。
この記事で制作したコードはコチラ
前提
nodeのバージョン: 20.11.1
Nest.jsプロジェクト作成
まず、Nest.jsのプロジェクトを作成します。
Nest.js Cliをインストール
npm i -g @nestjs/cli
Nest.jsプロジェクト作成
nest new nestjs-graphql-prisma-starter
この状態でnest.jsを起動したら、localhost:3000にアクセスし、「Hello world」と表示されたらOK!
npm run start
その他
以下のファイルは今回使わないので削除しておきましょう。
- app.controller.spec.ts
- app.controller.ts
- app.service.ts
- test/app.e2e-spec.ts
- test/jest-e2e.json
ローカル用データベース環境構築
ローカルで開発を行うためのデータベースを用意します。
docker-compose.ymlファイルを配置
以下のファイルをルート下に配置してください。
version: '3.8'
networks:
starter:
services:
starter-db:
image: mysql:8.0
env_file:
- ./docker/mysql.env
container_name: 'starter-db'
restart: 'no'
ports:
- 5306:3306
volumes:
- ./docker/conf/mysql.cnf:/etc/mysql/conf.d/mysql.conf.cnf
- ./docker/data:/var/lib/mysql
- ./docker/logs:/var/log/mysql
networks:
- starter
cap_add:
- SYS_NICE # CAP_SYS_NICE
今回は詳しい解説は省きますが、MySQL8.0のコンテナをホスト側ポートを5306、コンテナ側ポートを3306と指定して立てています。
ローカルDB用のenvファイルを配置
ルート下にdockerというフォルダを切り、中にmysql.envファイルを配置します。
MYSQL_ROOT_PASSWORD=password
MYSQL_PORT=5306
MYSQL_USER=starter-api
MYSQL_PASSWORD=password
MYSQL_DATABASE=starter-db
MYSQL_LOWER_CASE_TABLE_NAMES=0
dockerコンテナ立ち上げ
ここまでできたらコンテナを起動できます。
docker-compose up -d
正常に起動したかどうか確認してみましょう
docker ps -a
statusがUpとなっていればOKです!
Prisma導入
今回はORMとしてPrismaを使用します。以下のページを参考にしています。
Nest.js * Prisma公式ドキュメント
Prisma公式ドキュメント
Prismaセットアップ
まずはPrisma CLIを開発用パッケージとしてインストール。
npm install prisma --save-dev
ローカルでPrisma CLIを起動。
npx prisma
Prismaの初期化。
npx prisma init
するとルート下にprismaフォルダが作成され、中にschema.prismaファイルが入っているはずです。
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
このファイルを以下のように修正します。
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
datasource db {
- provider = "postgresql"
+ provider = "mysql"
url = env("DATABASE_URL")
}
また、.envファイルができているはずなので、その中にデータベースのURLを記述します。
DATABASE_URL="mysql://root:password@localhost:5306/starter-db"
これでPrisma〜DB間の設定は完了です。
テーブル作成
早速テーブルを作ってみましょう。
model User {
id String @id @default(cuid())
firebaseUId String @unique
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
todos Todo[]
}
enum TodoStatus {
// 未完了
NOT_STARTED
// 進行中
IN_PROGRESS
// 完了
COMPLETED
}
model Todo {
id String @id @default(cuid())
userId String
title String
description String
status TodoStatus @default(NOT_STARTED)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
User-Todoが1-多のリレーションを持っています。
また、onDelete: Cascadeを設定することにより、紐づくUserが削除された場合、Todoも自動的に削除されるようになります。
その他書き方については公式ドキュメントをご覧ください。
マイグレート
Prismaモデルを配置すると、そこからSQL移行ファイルを生成し、データベースに対して実行できます。この一連の処理をマイグレートと呼びます。
早速実行してみましょう。
※実行するとマイグレーションにつける名前を聞かれるので、適当に入力してください。
npx prisma migrate dev
成功するとprismaフォルダの下にmigration.sqlが作成されます。
-- CreateTable
CREATE TABLE `User` (
`id` VARCHAR(191) NOT NULL,
`firebaseUId` VARCHAR(191) NOT NULL,
`name` VARCHAR(191) NOT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `User_firebaseUId_key`(`firebaseUId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Todo` (
`id` VARCHAR(191) NOT NULL,
`userId` VARCHAR(191) NOT NULL,
`title` VARCHAR(191) NOT NULL,
`description` VARCHAR(191) NOT NULL,
`status` ENUM('NOT_STARTED', 'IN_PROGRESS', 'COMPLETED') NOT NULL DEFAULT 'NOT_STARTED',
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `Todo` ADD CONSTRAINT `Todo_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
prisma studioを使えばDBの状態を確認することができます。
npx prisma studio
localhost:5555
Prisma Clientのセットアップ
データベース構築までできるようになりました。
次はTypeScriptを使用してDB操作を行えるよう、Prisma Clientのセットアップを行います。
まずはPrisma Clientをインストールしましょう。
npm install @prisma/client
初回インストール時、npx prisma generate
が自動で行われることでPrismaスキーマからnode_modules/.prisma/client
へコードが生成されます。
以降、スキーマの変更を行った場合はnpx prisma generate
を再度行う必要があります。
Prisma Clientをインスタンス化できるようになったので、Nest.jsに組み込んでいきます。
src直下にprisma.service.tsファイルを作成しましょう。
import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
}
これでPrismaServiceからDB操作のためのメソッドを呼び出すことができるようになりました。
オマケ
prismaが発行するSQLを見えるようにしておいた方が後々開発効率が上がるので、やっておきましょう。
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { Prisma, PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService
extends PrismaClient<Prisma.PrismaClientOptions, Prisma.LogLevel>
implements OnModuleInit
{
private readonly logger = new Logger(PrismaService.name);
constructor() {
super({ log: ['query', 'info', 'warn', 'error'] });
}
async onModuleInit(): Promise<void> {
this.$on('query', (event) => {
this.logger.log(
`Query: ${event.query}`,
`Params: ${event.params}`,
`Duration: ${event.duration} ms`,
);
});
this.$on('info', (event) => {
this.logger.log(`message: ${event.message}`);
});
this.$on('error', (event) => {
this.logger.log(`error: ${event.message}`);
});
this.$on('warn', (event) => {
this.logger.log(`warn: ${event.message}`);
});
await this.$connect();
}
}
以下を参考にしました。
GraphQL導入
それではgraphqlを導入していきます。スキーマファーストとコードファーストの2通りのやり方がありますが、今回はコードファーストでいきます。
セットアップ
まず必要なパッケージをインストールします。
npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql
次にgraphqlモジュールをNestJSアプリケーションに組み込み初期化します。
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import * as path from 'path';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: path.join(process.cwd(), 'src/schema.gql'),
sortSchema: true,
}),
],
})
export class AppModule {}
autoSchemaFile
: 自動生成されたスキーマが作成されるパス
sortSchema
: デフォルトの場合、モジュールで定義された順にスキーマが並ぶ。trueにすると、辞書順に並ぶ。
これでgraphqlが使用できるようになりました!
アプリケーションを開発する準備が整ったので、次は機能を作っていきます。
Discussion