バックエンドを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