この記事について
graphqlで動くものを書き換えて試してみたい人向けです。
graphqlを使ったTodoListを用いて、graphqlの使い方を一部紹介します。言語はtypescriptを使っています。
特に、graphqlのフロントエンドとバックエンドの対応箇所の紹介となります。
yarn, nodejs, dockerあたりが動作する環境であれば動くと思います。
実行方法及び、詳しいバージョンや利用ライブラリ等は、各リポジトリのREADME.md及びpackage.jsonを参照してください。
フロントエンド
バックエンド
コードと紹介
ファイル名だけで補足している箇所もあるので、VSCodeを使える場合は、ctrl + P
or cmd + P
のファイル名検索を駆使して見ていただけると把握しやすいかと思います。
フロントエンドgraphql: fragments, queries, mutations
バックエンドで自動生成されるschema.graphql
を持ってきて、
frontend/src/graphql
以下のファイルを良い感じに書いてyarn run graphql-codegen
すると、frontend/src/api.ts
が生成され、
Reactコンポーネントから呼び出せる(frontend/src/components/TodoComponent.tsx: 12行目
付近参照)ようになります。
VSCodeを用いている場合は、Apollo GraphQL
拡張機能がオススメです。
schema.graphql
を元にfrontend/src/graphql
以下のファイルを書く時にエラーを出したりしてくれます。
schema.graphql
を更新しても反映されないことがありますが、その際は拡張機能を一度無効にして有効にすると反映されます。
fragments
他のgraphqlファイルで複数回呼ばれるような取得パターンがある場合にfragmentを作成することで記述量を削減できます。
graphqlAPI側が用意している戻り値の中からデータを選ぶ必要があり、今回だとbackend/src/models/todo/todo.ts
やschema.graphql: 5~11行目
から選ぶことができます。
fragment TodoItem on Todo {
id
title
isDone
}
queries
データを取得する場合に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
}
}
mutations
データを変更する場合はmutationを用います。
それ以外はqueriesと同様です。
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: resolver
バックエンドサーバーが稼働している間は、
@Query
アノテーションと直後のメソッド、@Mutation
アノテーションと直後のメソッドを元にbackend/src/schema.graphql
が自動更新されます。
frontend/src/graphql/queries/todo.graphql: 2行目
のtodos
と、以下ファイルの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部分です。
個人的にですが、graphqlAPIはコード自体にデータの送受信内容が明記されているため、処理がイメージしやすく開発時の負担が少なくなるように感じました。
特に、APIとの接続や実装の変更等が、処理がイメージしやすい分スムーズに行えるように思います。
メソッド名を書き換えてみたり、オリジナルの機能をつけ足してみたりして、graphqlを体験してみてください!