Closed10

Fastify, Apollo Server, Pothos で GraphQL サーバー構築手順メモ

siropacasiropaca

package.json はこんな感じ(一部省略)

{
  "name": "backend",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "run-script": "node --experimental-loader=ts-node/esm",
    "dev": "nodemon"
  },
  "nodemonConfig": {
    "exec": "node --env-file=.env --experimental-loader=ts-node/esm/transpile-only ./src/index.ts",
    "ext": "ts,js,json",
    "watch": [
      "src"
    ]
  },
  "dependencies": {
    "@apollo/server": "4.9.5",
    "@as-integrations/fastify": "2.1.1",
    "fastify": "4.24.3",
    "graphql": "16.8.1"
  },
  "devDependencies": {
    "@types/node": "20.9.0",
    "nodemon": "3.0.2",
    "ts-node": "10.9.1",
    "typescript": "5.2.2"
  }
}
siropacasiropaca

example とか参考にしながら src/index.ts を以下のようにしてみる。

import Fastify from "fastify"
import { ApolloServer, BaseContext } from "@apollo/server"
import fastifyApollo, { fastifyApolloDrainPlugin, fastifyApolloHandler } from '@as-integrations/fastify'

const typeDefs = `
  type Query {
    hello: String
  }
`

const resolvers = {
  Query: {
    hello: () => 'Hello world!',
  },
}

const app = Fastify({
  logger: true,
})

const apollo = new ApolloServer<BaseContext>({
  typeDefs,
  resolvers,
  plugins: [fastifyApolloDrainPlugin(app)],
})

await apollo.start()

await app.register(fastifyApollo(apollo))

try {
  await app.listen({ port: 65535 })
} catch (err) {
  app.log.error(err)
  process.exit(1)
}
siropacasiropaca

サーバーを起動し、/graphql にブラウザでアクセス。

$ pnpm run dev

Apollo Server の GraphQL Explorer が起動したら、Query を投げてレスポンスがかってくることを確認。

siropacasiropaca

その他必要そうなライブラリをインストール。

$ pnpm install @fastify/compress @fastify/cors @fastify/helmet @fastify/rate-limit

dependencies:
+ @fastify/compress 6.5.0
+ @fastify/cors 8.4.1
+ @fastify/helmet 11.1.1
+ @fastify/rate-limit 9.0.1

https://www.npmjs.com/package/@fastify/compress

https://www.npmjs.com/package/@fastify/cors

https://www.npmjs.com/package/fastify-helmet

https://www.npmjs.com/package/fastify-rate-limit

src/index.ts に設定。

...  

await apollo.start()

await app.register(helmet, {
  // false にしないと Explorer が使用できない (Production では true にする)
  contentSecurityPolicy: false,
})
await app.register(rateLimit)
await app.register(cors)
await app.register(compress)

...  
siropacasiropaca

一旦、build コマンドを追加する。

package.json

"scripts": {
  ...
  "build": "tsc"
},

tsconfig.json

{
  "extends": "tsconfig/backend.json",
  "compilerOptions": {
    "outDir": "./dist",
    "baseUrl": "./src",
    "rootDir": "./src"
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules"]
}

build コマンドを叩くと以下のようになる。

./
├── README.md
├── dist/
│   ├── context.js
│   ├── context.js.map
│   ├── index.js
│   ├── index.js.map
│   ├── resolvers.js
│   ├── resolvers.js.map
│   ├── type-defs.js
│   └── type-defs.js.map
├── package.json
├── src/
│   ├── context.ts
│   ├── index.ts
│   ├── resolvers.ts
│   └── type-defs.ts
├── tsconfig.json
└── tsconfig.tsbuildinfo
siropacasiropaca

Pothos GraphQL をインストールする。

$ pnpm install @pothos/core
dependencies:
+ @pothos/core 3.41.0

公式の Hello, World と examples を基に初期セットアップ。

https://pothos-graphql.dev/
https://github.com/hayes/pothos/tree/main/examples

./
├── ...
├── src/
│   ├── graphql/
│   │   ├── builder.ts
│   │   ├── context.ts
│   │   ├── schema.ts
│   │   └── server.ts
│   └── index.ts
└── tsconfig.json

graphql/builder.ts

import SchemaBuilder from '@pothos/core'

export const builder = new SchemaBuilder({})

graphql/schema.ts

import { builder } from './builder.js'

builder.queryType({
  fields: (t) => ({
    hello: t.string({
      args: {
        name: t.arg.string(),
      },
      resolve: (parent, { name }) => `hello, ${name || 'World'}`,
    }),
  }),
})

export const schema = builder.toSchema()

graphql/server.ts

import { ApolloServer } from '@apollo/server'
import { fastifyApolloDrainPlugin } from '@as-integrations/fastify'

import { schema } from './schema.js'

import type { Context } from './context'
import type { FastifyInstance } from 'fastify'

export const createApolloServer = ({ app }: { app: FastifyInstance }) => {
  return new ApolloServer<Context>({
    schema,
    plugins: [fastifyApolloDrainPlugin(app)],
  })
}

index.ts

...
import { createApolloServer } from './graphql/server.js'

const apollo = createApolloServer({ app })

await apollo.start()
...

引数を含めたレスポンスが返ってくることを確認。

siropacasiropaca

GraphQL Code Generator で GraphQL スキーマと TypeScript の型を出力する。

https://pothos-graphql.dev/docs/guide/generating-client-types

https://the-guild.dev/graphql/codegen/docs/getting-started/installation

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

$ pnpm install @graphql-codegen/cli @graphql-codegen/client-preset -D

codegen.ts を作成する。(パスは任意のパス)

https://the-guild.dev/graphql/codegen/docs/config-reference/codegen-config

codegen.ts

import { printSchema } from 'graphql'

import { schema } from './schema/index.js'

import type { CodegenConfig } from '@graphql-codegen/cli'

const config: CodegenConfig = {
  schema: printSchema(schema),
  generates: {
    'path/to/file.graphql': {
      plugins: ['schema-ast'],
    },
    'path/to/': {
      preset: 'client',
      plugins: [],
    },
  },
}

export default config

現在のディレクトリ構成

./
├── ...
├── package.json
├── src/
│   ├── graphql/
│   │   ├── codegen.ts
│   │   ├── context.ts
│   │   ├── data.ts
│   │   ├── schema/
│   │   │   ├── builder.ts
│   │   │   ├── comment.ts
│   │   │   ├── index.ts
│   │   │   ├── post.ts
│   │   │   └── user.ts
│   │   └── server.ts
│   └── index.ts
└── tsconfig.json

package.jsongenerate コマンドを追加。

package.json

{
  ...
  "scripts": {
    ...
    "generate": "graphql-codegen --config ./src/graphql/codegen.ts"
  },
  ...
}

generate コマンドを叩き、ts ファイルと graphql スキーマが出力されることを確認。

./path/to/
├── fragment-masking.ts
├── gql.ts
├── graphql.ts
├── index.ts
└── schema.graphql
このスクラップは2023/12/25にクローズされました