バックエンドをgqlgenで実装して
graphql-codegenでスキーマ情報からTypeScriptのtypeを自動生成する構成で実装したました。
実装
gqlgenでバックエンドを実装
■ Pull Request https://github.com/wheatandcat/PeperomiaBackend/pull/23/files
まずは、以下のチュートリアルから、そのまま実装
$ go get github.com/99designs/gqlgen $ go run github.com/99designs/gqlgen init
これで初期ファイルが生成されるので、まずは以下を参考にgin側のHandlerを追加
Using Gin to setup HTTP handlers — gqlgen
次に、実装に合わせてschema.graphqlsを以下の通りに修正
■ graph/schema.graphqls
type Item {
id: ID!
title: String!
kind: String!
itemDetails: [ItemDetail]!
calendar: Calendar!
}
type Calendar {
id: ID!
itemId: String!
date: String!
}
type ItemDetail {
id: ID!
title: String!
itemId: String!
kind: String!
moveMinutes: Int!
place: String!
url: String!
memo: String!
priority: Int!
}
type Query {
item(id: ID!): Item!
}
今回は更新は無いのでMutationは無しで実装していきます
schema.graphqlsを書き換えたら以下のコマンドを実行して再生成
$ go run github.com/99designs/gqlgen generate
これでスキーマの情報にあせてtypeとResolverを自動生成してくれます
■ graph/model/models_gen.go
package model
type Calendar struct {
ID string `json:"id"`
ItemID string `json:"itemId"`
Date string `json:"date"`
}
type Item struct {
ID string `json:"id"`
Title string `json:"title"`
Kind string `json:"kind"`
ItemDetails []*ItemDetail `json:"itemDetails"`
Calendar *Calendar `json:"calendar"`
}
type ItemDetail struct {
ID string `json:"id"`
Title string `json:"title"`
ItemID string `json:"itemId"`
Kind string `json:"kind"`
MoveMinutes int `json:"moveMinutes"`
Place string `json:"place"`
URL string `json:"url"`
Memo string `json:"memo"`
Priority int `json:"priority"`
}
あとはResolverにFirestoreからデータを取得するコードを追加して完成
■ graph/schema.resolvers.go
package graph
import (
"context"
"github.com/wheatandcat/PeperomiaBackend/graph/generated"
"github.com/wheatandcat/PeperomiaBackend/graph/model"
)
func (r *queryResolver) Item(ctx context.Context, id string) (*model.Item, error) {
h := r.Handler
item := &model.Item{}
i, err := h.App.ItemRepository.FindByPublicAndID(ctx, h.FirestoreClient, id)
if err != nil {
return item, err
}
c, _ := h.App.CalendarRepository.FindByItemID(ctx, h.FirestoreClient, id)
ids, _ := h.App.ItemDetailRepository.FindByItemID(ctx, h.FirestoreClient, id)
item = i.ToModel()
item.Calendar = c.ToModel()
for _, id := range ids {
item.ItemDetails = append(item.ItemDetails, id.ToModel())
}
return item, nil
}
// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
type queryResolver struct{ *Resolver }
これで実際にAPIを叩くと以下みたいな感じで取得できるようになります

graphql-codegenでtypeを自動生成
先程、作成したschema.graphqlsの元にgraphql-codegeでtypeファイルを生成します。
■ Pull Request
https://github.com/wheatandcat/PeperomiaWeb/pull/47
まずは以下のインストールする
$ yarn add -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations
まず、以下のコマンドをpackage.jsonに追加
■ package.json
"scripts": {
...
"download:schema.graphqls": "curl -L -O https://raw.githubusercontent.com/wheatandcat/PeperomiaBackend/master/graph/schema.graphqls",
"codegen": "npm run download:schema.graphqls && graphql-codegen",
"codegen:lint:fix": "eslint --fix ./queries/types/index.d.ts"
},
以下の設定ファイルを作成
■ codegen.yml
overwrite: true
schema:
- ./schema.graphqls
documents:
- "./queries/**/*.gql"
generates:
./queries/types/index.d.ts:
hooks:
afterOneFileWrite:
- yarn codegen:lint:fix
plugins:
- typescript
- typescript-operations
config:
skipTypename: true
preResolveTypes: true
そして、Web画面で使用するGraphQLファイルを設置
■ queries/shareItem.gql
query ShareItem($itemId: ID!) {
item(id: $itemId) {
id
title
kind
itemDetails {
id
title
kind
moveMinutes
place
url
memo
priority
}
calendar {
id
date
}
}
}
ここまで準備完了です。あとは以下のコマンドを実行
$ yarn codegen
すると、スキーマ情報とgqlファイルから以下のtypeファイルが生成されます
■ queries/types/index.d.ts
export type Maybe<T> = T | null
export type Exact<T extends { [key: string]: any }> = { [K in keyof T]: T[K] }
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: string
String: string
Boolean: boolean
Int: number
Float: number
}
export type Calendar = {
id: Scalars['ID']
itemId: Scalars['String']
date: Scalars['String']
}
export type Item = {
id: Scalars['ID']
title: Scalars['String']
kind: Scalars['String']
itemDetails: Array<Maybe<ItemDetail>>
calendar: Calendar
}
export type ItemDetail = {
id: Scalars['ID']
title: Scalars['String']
itemId: Scalars['String']
kind: Scalars['String']
moveMinutes: Scalars['Int']
place: Scalars['String']
url: Scalars['String']
memo: Scalars['String']
priority: Scalars['Int']
}
export type Query = {
item: Item
}
export type QueryItemArgs = {
id: Scalars['ID']
}
export type ShareItemQueryVariables = Exact<{
itemId: Scalars['ID']
}>
export type ShareItemQuery = {
item: {
id: string
title: string
kind: string
itemDetails: Array<
Maybe<{
id: string
title: string
kind: string
moveMinutes: number
place: string
url: string
memo: string
priority: number
}>
>
calendar: { id: string; date: string }
}
}
@nuxtjs/apolloでWeb側を実装
上記で作成したtypeを使用してnuxtjs/apolloで実装
nuxt.config.jsに以下を追加
■ nuxt.config.js
modules: ['@nuxt/http', '@nuxtjs/firebase', '@nuxtjs/apollo'],
...
apollo: {
tokenName: 'yourApolloTokenName',
cookieAttributes: {
expires: 7,
},
includeNodeModules: true,
clientConfigs: {
default: '~/plugins/apollo-client.ts',
},
},
これnuxtjs/apolloが使用できるので、composition apiを使用してAPI取得部分を作成
■ use/useShareItem.ts
import { reactive, SetupContext, toRefs } from '@vue/composition-api'
import shareItemQuery from '~/queries/shareItem.gql'
import { ShareItemQuery, ShareItemQueryVariables } from '~/queries/types'
type UseFetchShareItemState = {
item: ShareItemQuery['item'] | null
loading: boolean
}
const useFetchShareItem = (ctx: SetupContext) => {
const state = reactive<UseFetchShareItemState>({
item: null,
loading: true,
})
const fetchShareItem = async (itemId: string) => {
state.loading = true
const res = await ctx.root.$apollo.query<
ShareItemQuery,
ShareItemQueryVariables
>({
query: shareItemQuery,
fetchPolicy: 'network-only',
variables: { itemId },
})
state.item = res.data.item
state.loading = false
}
return {
...toRefs(state),
fetchShareItem,
}
}
export default useFetchShareItem
graphql-codegenで自動生成したtypeは以下のように指定すればOKです
import { ShareItemQuery, ShareItemQueryVariables } from '~/queries/types'
...
const res = await ctx.root.$apollo.query<
ShareItemQuery,
ShareItemQueryVariables
>({
query: shareItemQuery,
fetchPolicy: 'network-only',
variables: { itemId },
})
これでGraphQLの型を自動生成しつつフロントエンドのコード作成が行えるようになりました。
上記を利用して、以下のwebページを作成しました。
https://app.peperomia.info/share/1601cad6-a3f9-4df3-8de9-7a08fea6f35f
