使用Next.js + GraphQL构建Todo应用程序(中篇)

首先

最近我学习了GraphQL,利用Next.js + GraphQL + GraphQL CodeGenerator + Prisma搭建了一个Todo应用程序,现在我会简单介绍一下,并在这里记录下来。

スクリーンショット 2023-04-15 20.58.42.png

环境

    • 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架构

.
├── src
│   ├── graphql
│   │   ├── typeDefs
│   │   │   ├─ common.graphql
│   │   │   └─ todo.graphql
│   │   │

我們將在這樣的文件結構下進行創建。
為了將來增加模式時的方便,我們打算在common.graphql中定義共用的模式。

schema

Query

ListTodos: Todo一覧を取得する

Mutation

addTodo: contentを元にTodoを作成する
updateTodo: idとdoneから、todoの状態を更新する
deleteTodo: idから、todoを削除する

create file
$ mkdir src/graphql src/graphql/typeDefs
$ touch src/graphql/typeDefs/todo.graphql src/graphql/typeDefs/common.graphql

edit file

todo.graphql
type 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.graphql
scalar DateTime

2. GraphQL上下文的配置

.
├── src
│   ├── graphql
│   │   ├── context
│   │   │   ├── index.ts

create file
$ mkdir src/graphql/context
$ touch src/graphql/context/index.ts

edit file

src/graphql/context/index.ts
import { PrismaClient } from “@prisma/client”;

const prisma = new PrismaClient();

export type Context = {
prisma: typeof prisma;
};

export const createContext = () => {
return {
prisma: prisma,
};
};

3. GraphQL 代码生成器

 

.
├── src
│   ├── graphql
|   |   ├── typeDefs
|   |   |   ├── common.graphql
|   |   |   └── todo.graphql
│   │   ├── context
│   │   │   ├── index.ts
虽然也提供了init命令,但为了安装插件等,我们将进行手动设置,并继续进行操作。

 

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.yml
generates:
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 解析器

.
├── 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.ts
import { 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 },
});
},
},
};

スクリーンショット 2023-04-16 10.32.37.png

5. 修复GraphQL服务器

将通过GraphQL Code Generator生成的文件和使用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)。

スクリーンショット 2023-04-15 0.53.15.png

看起来动了起来?
这样就完成了GraphQL服务器的设置。

下一次

接下来,我们将进行前端设置。

 

    • 後編

フロント側準備
フロント側実装

bannerAds