基于redux、apollo、nestjs和graphql创建的TodoList,这里部分介绍了graphql的使用方法
关于这篇文章
这是为那些想试着将运行在GraphQL上的东西进行修改的人准备的。
使用GraphQL构建的TodoList,介绍了GraphQL的部分用法。我们使用Typescript语言。
特别是涉及到GraphQL前端和后端配合的部分。
如果是在支持yarn、nodejs和docker等环境下,我认为它可以运行。
请参考各个存储库的README.md和package.json,了解详细的执行方法、版本以及使用的库等信息。
前端
后端
介绍与代码
因为有一些地方仅用文件名进行补充,所以如果您使用VSCode,可以利用ctrl + P或cmd + P的文件名搜索来更容易理解。
前端GraphQL:片段、查询、变更
将自动生成的schema.graphql从后端拿到,然后将frontend/src/graphql目录下的文件写得漂亮一些,运行yarn run graphql-codegen,就能生成frontend/src/api.ts文件,从而可以在React组件中调用它(参考frontend/src/components/TodoComponent.tsx的第12行左右)。
如果您使用VSCode,我推荐使用Apollo GraphQL扩展功能。它可以帮助您在编写frontend/src/graphql文件时,根据schema.graphql显示错误信息。有时候,即使更新了schema.graphql,更改可能不会生效,但是您可以尝试禁用然后重新启用扩展功能,以使更改生效。
片段
如果有在其他graphql文件中被多次调用的获取模式,通过创建fragment可以减少代码量。
需要从graphqlAPI返回的数据中选择数据,本例中可以从backend/src/models/todo/todo.ts和schema.graphql的5~11行中进行选择。
fragment TodoItem on Todo {
id
title
isDone
}
查询
当获取数据时,使用查询(query)语句。
要获取的对象可以是数组也可以是对象(如[Todo]或Todo),在查询(query)或变更(mutation)语句中都需要以对象形式进行描述。
需要将类似于文件的第2行和第8行中的内部todos和todo与API保持一致,但是可以将第1行和第7行中的外部todos和todo改写为getTodos和getTodo。在这种情况下,需要在React组件中将useTodosQuery更改为useGetTodosQuery。
另外,也可以将文件分割并进行记录,没有任何问题。
query todos {
todos {
...TodoItem
}
}
query todo($id: ID!) {
todo(id: $id) {
...TodoItem
}
}
以下的改变方式是可行的。
query getTodo($id: ID!) {
todo(id: $id) {
id
title
isDone
createdAt
updatedAt
}
}
query getTodos {
todos {
id
title
isDone
}
}
突变
如果要更改数据,使用mutation。除此之外与查询相同。
mutation createTodo($input: TodoInput!) {
createTodo(input: $input) {
...TodoItem
}
}
mutation updateTodo($id: ID!, $input: TodoInput!) {
updateTodo(id: $id, input: $input) {
...TodoItem
}
}
mutation deleteTodo($id: ID!) {
deleteTodo(id: $id)
}
后端GraphQL:解析器
只要后端服务器正在运行,@Query注释和其后的方法、@Mutation注释和其后的方法将自动更新backend/src/schema.graphql文件。
frontend/src/graphql/queries/todo.graphql文件的第2行对应着以下文件的第46行的方法名todos。
import { Resolver, Query, Args, ID, Mutation } from '@nestjs/graphql'
import { BadRequestException } from '@nestjs/common'
import Todo from '../models/todo/todo'
import { TodoService } from './todo.service'
import { DBService } from '../db/db.service'
import TodoInput from '../models/todo/todo.input'
@Resolver()
export class TodoResolver {
constructor(
private readonly dbservice: DBService,
private readonly service: TodoService,
) {}
getClient() {
return this.dbservice.getClient()
}
@Query(
returns => Todo,
{
description: 'todoを取得',
},
)
async todo(
@Args({ name: 'id', type: () => ID }) id: string
): Promise<Todo> {
const client = this.getClient()
const res = await this.service.get(client, id)
if (!res) {
throw new BadRequestException(`Todo Not Found id: ${id}`)
}
return res
}
@Query(
returns => [Todo],
{
description: 'todo一覧を取得',
},
)
async todos(): Promise<Todo[]> {
const client = this.getClient()
const res = await this.service.scan(client)
return res
}
@Mutation(
returns => Todo,
{
description: 'todoを作成',
},
)
async createTodo(@Args('input') input: TodoInput): Promise<Todo> {
const client = this.getClient()
const res = await this.service.create(client, input)
return res
}
@Mutation(
returns => Todo,
{
description: 'todoを更新',
},
)
async updateTodo(
@Args({ name: 'id', type: () => ID }) id: string,
@Args('input') input: TodoInput,
): Promise<Todo> {
const client = this.getClient()
const res = await this.service.get(client, id)
if (!res) {
throw new BadRequestException(`Todo Not Found id: ${id}`)
}
return await this.service.update(client, {
...res,
...input,
})
}
@Mutation(
returns => String,
{
description: 'todoを削除',
},
)
async deleteTodo(
@Args({ name: 'id', type: () => ID }) id: string,
): Promise<string> {
const client = this.getClient()
const res = await this.service.get(client, id)
if (!res) {
throw new BadRequestException(`Todo Not Found id: ${id}`)
}
await this.service.delete(client, id)
return id
}
}
总结
这是主要的GraphQL部分。
个人认为,GraphQL API通过在代码本身明确了数据的发送和接收内容,使得处理过程更易于理解,减轻了开发负担。特别是在与API的连接和实现的变更方面,处理更易于理解,能够顺利进行。
尝试修改方法名称,增加原始功能,体验一下GraphQL!