TypeScript + Prisma + NestJSでGraphQLサーバを作ってみる 

## はじめに
TypeScriptとPrismaとNextJSを使ってGraphQLサーバーの作成を行います。

## 1.NestJSプロジェクトの作成
```
npm install -g @nestjs/cli
nest new my-app
```

インストール後、サーバを起動してみます。
```
cd my-app
npm run start
```
http://localhost:3000　
にアクセスすると　Hello World!　が表示されます。

## 2.Prismaのセットアップ

```
npm install prisma --save-dev　　　　　　//Prismaのパッケージをインストール
npm install @prisma/client
npx prisma init   　　　　　　　　　　　　//prismaの初期化
```
 prisma/schema.prisma  と .envファイルが生成されるので接続したいデータベースに合わせて設定していきます。

 今回は、supabaseを使用します。

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

datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")
}
```

```.env
DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA"
```

## 3.テーブルの作成
今回はタイトルと本文と公開フラグのカラムを持った Post テーブルを作成します。

```ruby:prisma/schema.prisma
model Post {
  id        Int     @default(autoincrement()) @id
  title     String
  content   String
  published Boolean @default(false)
}
```

以下のコマンドを実行し、マイグレーションファイルの生成とマイグレーションの実行します。

```
npx prisma migrate dev --name post
```

## 4. Prisma Studio でデータ追加してみる
PrismaにはPrisma Studioというデータベースに対してCRUDの操作ができるGUIが付いています。

以下のコマンドでPrisma Studioを起動して、レコードを追加してみましょう。

```
npx prisma studio
```
http://localhost:5555 にアクセスして Post テーブルにレコードを追加してみてください。

## 5.NestJSでPrismaクライアントを使用する

NestJSでPrismaクライアントを使用するために、src 配下に prisma.service.ts というファイルを作成し、次のコードを追加します。

、module,テストのファイルではproviderに記述しなければエラーが出る。


[参考ドキュメント](https://docs.nestjs.com/recipes/prisma#:~:text=Inside%20the%20src%20directory%2C%20create%20a%20new%20file%20called%20prisma.service.ts%20and%20add%20the%20following%20code%20to%20it%3A)

```ruby: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操作のためのメソッドを呼び出すことができるようになりました。


## 6.GraphQLのパッケージをインストール
NestJSの [GraphQLのページ](https://docs.nestjs.com/graphql/quick-start#installation:~:text=Design%20and%20Development,Installation)を参考に必要なパッケージをインストールします。

```
npm install @nestjs/graphql @nestjs/apollo graphql apollo-server-express
```

## 7.NestJSのGraphQLプラグインの有効化
NestJSのGraphQLプラグインを有効化するとコードをシンプルにできるので、`nest-cli.json` に `compilerOptions` を追加して有効化しておきます。

NestJS で GraphQL サーバーを開発の開発には、コードファーストとスキーマファーストのアプローチがあります。どちらを採用しても、「Schema、データ、Resolver を用意すれば GraphQL サーバーは動かせる」という原理に変わりありません。この選択は、Schema をどうやって作成するか という違いです。

今回はコードファーストのアプローチを取ります。
コードファーストのアプローチでは、TypeScriptのクラスとデコレータを使用してクラス定義からGraphQLのSDLを生成します。
このプラグインはコードファーストのアプローチのみ適用されます。


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

```
[参考ドキュメント](https://docs.nestjs.com/graphql/cli-plugin#:~:text=Using%20the%20CLI%20plugin%23)

## 8.テーブルの各データをモデルとして定義
今回は一覧を取得、レコード新規作成したいので以下のようなモデルを実装します。
このクラスはレスポンスのシリアライザかつschema.gql自動生成のための情報です。

現時点のPostのDBテーブルの構造に合わせてPost のObject Typeを作成してみましょう。
Post クラスを作り、`@ObjectType()` のデコレータを付けます。

`@nestjs/graphql`モジュールから必要なデコレーターと型をインポートしています。

`npm runstart:dev`を実行した際に、`app.module.ts`の`autoSchemaFile`で設定した場所に、自動的に`schema.gql`が作成されます。


```ruby:src/post/models/post.model.ts
import { Field, ID, ObjectType } from '@nestjs/graphql';

@ObjectType()
export class Post {
  @Field(() => ID)
  id: number;
  title: string;
  content: string;
  published: boolean;
}

```
GraphQLプラグインを有効化していない場合は各プロパティに `@Field()` を付ける必要がありますが、有効化している場合は`@Field()`を勝手につけてくれます。

number はデフォルトではGraphQLの Float に変換されるため、ここでは ID になるよう明示的に `@Field(() => ID)` を指定しています。
[参照](https://docs.nestjs.com/graphql/resolvers#:~:text=number%E5%9E%8B%E3%81%8C%E4%BD%BF%E7%94%A8%E3%81%95%E3%82%8C%E3%82%8B%E3%81%9F%E3%82%81%E3%80%81%E8%AA%B2%E9%A1%8C%E3%81%8C%E7%94%9F%E3%81%98%E3%81%BE%E3%81%99%E3%80%82TypeScriptnumber%E5%9E%8B%E3%81%A7%E3%81%AF%E3%80%81%E6%83%B3%E5%AE%9A%E3%81%95%E3%82%8C%E3%82%8B%20GraphQL%20%E8%A1%A8%E7%8F%BE%20(%20%E3%81%A8Int%E3%81%AA%E3%81%A9)%20%E3%81%AB%E9%96%A2%E3%81%99%E3%82%8B%E5%8D%81%E5%88%86%E3%81%AA%E6%83%85%E5%A0%B1%E3%81%8C%E5%BE%97%E3%82%89%E3%82%8C%E3%81%BE%E3%81%9B%E3%82%93%E3%80%82%E3%81%97%E3%81%9F%E3%81%8C%E3%81%A3%E3%81%A6%E3%80%81%E5%9E%8B%E5%8F%82%E7%85%A7%E3%82%92%E6%98%8E%E7%A4%BA%E7%9A%84%E3%81%ABFloat%E6%B8%A1%E3%81%99%E5%BF%85%E8%A6%81%E3%81%8C%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82%E3%81%93%E3%82%8C%E3%81%AF%E3%80%81%E6%AC%A1%E3%81%AB%E7%A4%BA%E3%81%99%E3%82%88%E3%81%86%E3%81%AB%E3%80%81%E5%BC%95%E6%95%B0%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E5%90%AB%E3%82%802%20%E7%95%AA%E7%9B%AE%E3%81%AE%E5%BC%95%E6%95%B0%E3%82%92%E3%83%87%E3%82%B3%E3%83%AC%E3%83%BC%E3%82%BF%E3%81%AB%E6%B8%A1%E3%81%99%E3%81%93%E3%81%A8%E3%81%A7%E8%A1%8C%E3%81%84%E3%81%BE%E3%81%99%E3%80%82Args())



## 9.Resolverの作成
続いて、GraphQLでデータ操作を行うためのResolverを作成していきます。
今回は、Post の一覧を取得するQueryとレコードを新規作成するMutationを作成します。

Resolverを作るには、Resolverクラスを作成し、`@Resolver()` のデコレータを付けます。
そして、QueryとMutationの関数には `@Query()` と `@Mutation()`、引数には `@Args()` を付けます。
これらの情報からGraphQLのスキーマが生成されます。


``` ruby:src/posts.post.resolver.ts
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { Post } from './models/post.model';
import { PrismaService } from 'src/prisma.service';

@Resolver(() => Post)
export class PostResolver {
  constructor(private prisma: PrismaService) {}

@Query(() => [Post])
async posts() {
  return this.prisma.post.findMany();
}

@Mutation(() => Post)
async createPost(
  @Args('title') title: string,
  @Args('content') content: string,
) {
  return this.prisma.post.create({ data: { title, content} });
}
}
```

`constructor(private prisma: PrismaService) {}`　でインスタンス化しています。

QueryやMutationの関数の中では、PrismaClientを使ってレコードの取得や作成を行っています。

## 10. AppModuleに必要なモジュールとクラスを追加

graphqlモジュールをNestJSアプリケーションに組み込み初期化します。
AppModule に GraphQLModule と先程作成した PrismaService、 PostResolver を追加します。

```ruby:src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { PrismaService } from './prisma.service';
import { PostResolver } from './posts/posts.resolver';
import { join } from 'path';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
    }),
  ],
  controllers: [AppController],
  providers: [AppService, PrismaService, PostResolver],
})
export class AppModule {}
```

GraphQLModuleの`forRoot()`には GraphQL のオプションを設定できます。
`autoSchemaFile: join(process.cwd(), 'src/schema.gql')` は `autoSchemaFileプロパティ`によって、プロジェクトのsrcディレクトリに自動的にGraphQLスキーマファイルが生成されるようにしています。
モデルを定義したファイルに適当なデコレータを付与することで、自動的にgqlファイルを作成するようにできます。

この設定により、NestJSアプリケーションにGraphQLのサポートが追加されます。

<details><summary>Moduleの補足</summary>

@Moduleデコレータのプロパティ
・providers: @Injectableデコレータが付いたクラスを記述
・controllers @Controllerデコレータが付いたクラスを記述
・imports: モジュール内部で必要な外部モジュールを記述
・exports: 外部のモジュールにエクスポートしたいもの
</details>


<details><summary>参考ドキュメント</summary>

[参照](https://docs.nestjs.com/graphql/quick-start#:~:text=OFFICIAL%20GRAPHQL%20EXTENSIONS-,Getting%20started%20with%20GraphQL%20%26%20TypeScript,-%23)

[参照](https://docs.nestjs.com/graphql/quick-start#:~:text=TypeScript%E3%81%A8GraphQL,%E3%82%92%E6%9B%B8%E3%81%8F%23)

</details>

## 動作確認

```
npm run start
```

Queryで記事の一覧を取得してみます。
<details><summary>クエリ</summary>
```
query {
  posts {
    id
    title
    content
    published
  }
}
```
</details>


記事の作成をしてみます。
<details><summary>クエリ</summary>
```
mutation {
  createPost(title: "piyo", content: "3記事目の本文です。") {
    id
    title
    content
    published
  }
}
```
</details>









## 参照
https://zenn.dev/rince/articles/50a66241d04f0b

https://zenn.dev/mano_r/articles/8d25be4b4452dd

https://zenn.dev/kou_kawa/articles/30-graphql-nextjs-nestjs

https://qiita.com/marumaru0113/items/9bc72335439ed135b030

https://zenn.dev/azunasu/articles/28f797ce2405e7

https://en-ambi.com/itcontents/entry/2023/02/17/093000/

https://web-tips.2rta.com/nestjs-prisma-crud/

https://developers-book.com/2024/06/23/1196/