はじめに
最近GraphQLについて学びまして、
Next.js + GraphQL + GraphQL CodeGenerator + Prismaの構成でTodoアプリを構築したので
軽〜く解説をしつつ、その記録をここに残します。
環境
- Macbook Air
- node
- v18.13.0
- pnpm
- 7.27.0
目次
-
前編
- Next Create App
- GraphQLサーバー構築
- Subscription
- DB・Prisma
-
中編 👈 今ここ
- GraphQL Schema
- GraphQL Context
- GraphQL Code Generator
- GraphQL Resolver
- GraphQLサーバー修正
-
後編
- フロント側準備
- フロント側実装
中編
中編はGraphQLフェーズです
1. GraphQL Schema
.
├── src
│ ├── graphql
│ │ ├── typeDefs
│ │ │ ├─ common.graphql
│ │ │ └─ todo.graphql
│ │ │
このようなファイル構成で作成します。
common.graphql
には今後schemaが増えてきた時のために、共通のschemaを定義しようと思います。
-
schema
- Query
- ListTodos: Todo一覧を取得する
- Mutation
- addTodo: contentを元にTodoを作成する
- updateTodo: idとdoneから、todoの状態を更新する
- deleteTodo: idから、todoを削除する
- Query
-
create file
$ mkdir src/graphql src/graphql/typeDefs $ touch src/graphql/typeDefs/todo.graphql src/graphql/typeDefs/common.graphql
-
edit file
todo.graphqltype Todo { id: ID! content: String! done: Boolean! createdAt: DateTime } type Query { listTodos: [Todo!]! } type Mutation { addTodo(content: String!): Todo! updateTodo(id: ID!, done: Boolean!): Todo! deleteTodo(id: ID!): Todo! } fragment TodoFragment on Todo { id content done createdAt } query ListTodos { listTodos { ...TodoFragment } } mutation AddTodo($content: String!) { addTodo(content: $content) { ...TodoFragment } } mutation UpdateTodo($id: ID!, $done: Boolean!) { updateTodo(id: $id, done: $done) { ...TodoFragment } } mutation DeleteTodo($id: ID!) { deleteTodo(id: $id) { ...TodoFragment } }
common.graphqlscalar DateTime
2. GraphQL Contextの設定
.
├── src
│ ├── graphql
│ │ ├── context
│ │ │ ├── index.ts
-
create file
$ mkdir src/graphql/context $ touch src/graphql/context/index.ts
-
edit file
src/graphql/context/index.tsimport { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); export type Context = { prisma: typeof prisma; }; export const createContext = () => { return { prisma: prisma, }; };
3. GraphQL Code Generator
.
├── src
│ ├── graphql
| | ├── typeDefs
| | | ├── common.graphql
| | | └── todo.graphql
│ │ ├── context
│ │ │ ├── index.ts
initのコマンドも用意されていますが、pluginsを入れたりしたいので、マニュアルセットアップしていきます
-
install
# dependencies $ pnpm add graphql @graphql-tools/graphql-file-loader @graphql-tools/load @graphql-tools/schema # devDependencies pnpm add -D @graphql-codegen/cli @graphql-codegen/schema-ast @graphql-codegen/typescript @graphql-codegen/typescript-resolvers @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo ts-node
-
create codegen.yml
$ touch codegen.yml
codegen.ymlgenerates: src/generated/schema.graphql: schema: - "src/graphql/typeDefs/**/*.graphql" plugins: - schema-ast src/generated/resolvers-types.ts: schema: - "src/graphql/typeDefs/**/*.graphql" plugins: - typescript - typescript-resolvers config: contextType: "@/graphql/context/#Context" mapperTypeSuffix: Model mappers: Todo: "@prisma/client#Todo" scalars: DateTime: string src/generated/request.ts: schema: - "src/graphql/typeDefs/**/*.graphql" documents: - "src/graphql/typeDefs/**/*.graphql" plugins: - typescript - typescript-operations - typescript-react-apollo config: scalars: DateTime: string
-
run graphql-codegen
$ pnpm graphql-codegen ✔ Parse Configuration ✔ Generate outputs
実行すると以下のような構成でresolver用の型定義ファイルなどが生成されます
. ├── src │ ├── generated │ │ ├── request.ts │ │ ├── resolvers-type.ts │ │ └── schema.graphql
4. GraphQL Resolver
.
├── src
│ ├── graphql
│ │ ├── resolvers
│ │ │ └─ index.ts
│ │ │
-
create file
$ mkdir src/graphql/resolvers $ touch src/graphql/resolvers/index.ts
-
edit file
3. GraphQL Code Generatorで作成した
resolvers-types
を利用してresolverを設定していきます。src/graphql/resolvers/index.tsimport { Resolvers } from "@/generated/resolvers-types"; export const resolvers: Resolvers = { Query: { listTodos: async (_parent, _args, { prisma }) => { return await prisma.todo.findMany(); }, }, Mutation: { addTodo: async (_parent, { content }, { prisma }) => { return await prisma.todo.create({ data: { content, createdAt: new Date().toISOString() }, }); }, updateTodo: async (_parent, { id, done }, { prisma }) => { return await prisma.todo.update({ where: { id }, data: { done }, }); }, deleteTodo: async (_parent, { id }, { prisma }) => { return await prisma.todo.delete({ where: { id }, }); }, }, };
5. GraphQLサーバー修正
3. GraphQL Code Generator及び、4. GraphQL Resolverで作成したファイルをインポートしていきます。
import { createYoga, createSchema } from "graphql-yoga";
import { readFileSync } from "fs";
import { join } from "path";
import { resolvers } from "@/graphql/resolvers";
import { createContext } from "@/graphql/context";
const path = join(process.cwd(), "src", "generated", "schema.graphql");
const typeDefs = readFileSync(path).toString("utf-8");
const schema = createSchema({
typeDefs,
resolvers,
})
const graphqlEndpoint = "/api/graphql";
export default createYoga({
graphqlEndpoint,
schema,
context: createContext,
});
localhost:3000/api/graphqlにアクセスし、Queryを叩いてみましょう。
動いてそうですね👌
これでGrapQLサーバーの設定が完了です。
次回
次はフロント側の設定をしていきます。
- 後編
- フロント側準備
- フロント側実装