React + Go + GraphQL + urqlを最小構成で試す
概要
手元にGraphQLが動かせる環境が欲しかったので最小構成で作ってみました。
せっかくだからとApollo Client
ではなくurql
を使ってみようとしたところgraphql-codegen
周りでちょっと手が止まったのでメモがてら記事にします。
完成版はこちら
環境
- Node.js 16.16.0
- yarn 1.22.10
- Go 1.17
server
基本的にgqlgenの公式ドキュメント通りに実行していきます。
go mod init
go mod init github.com/${myname}/xxx-app
tools.go
を用意
gqlgenはcode generateの時だけに使用するのでこうしないとgo mod tidy
で消されてしまいます。
//go:build tools
// +build tools
package tools
import (
_ "github.com/99designs/gqlgen"
)
雛形を生成
gqlgenのinitをします。これにより雛形が自動生成されます。
go run github.com/99designs/gqlgen init
繋ぎこみの準備
フロントと繋ぎ込みが行えるように生成されたschema.resolvers.go
のpanic部分を適当に変更します。
必要に応じて、ここにDBからの取得処理などを書いていきますが、最小構成としたいのでここではstructをハードコーディングしています。
// Todos is the resolver for the todos field.
func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
return []*model.Todo{
{ID: "1", Text: "first todo", Done: false},
{ID: "2", Text: "second todo", Done: true},
}, nil
}
さらにこのままだとフロント(localhost)からリクエストを送るとCORSエラーになるのでserver.go
に追加でcors対応を実装します。ここでは細かい設定は省略します。
package main
import (
"log"
"net/http"
"os"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/purp1eeeee/graphql-app/graph"
"github.com/purp1eeeee/graphql-app/graph/generated"
"github.com/rs/cors"
)
const defaultPort = "8080"
func main() {
port := os.Getenv("PORT")
if port == "" {
port = defaultPort
}
mux := http.NewServeMux()
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))
handler := cors.Default().Handler(mux)
mux.Handle("/", playground.Handler("GraphQL playground", "/query"))
mux.Handle("/query", srv)
log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
log.Fatal(http.ListenAndServe(":"+port, handler))
}
go run server.go
でサーバーを立ち上げてgraphiQLからtodos queryが動くことが確認できると思います。
サーバーはここまでで準備完了です。
front
雛形を作成
今回はviteのテンプレートを使用しますが、Next.jsでもcreate-react-appでもなんでも大丈夫です。
あとyarnじゃなくても大丈夫です。
yarn create vite client --template react-ts
graphQLのスキーマからTypeScriptの型情報を生成
ここではgraphql-code-generatorを使用します。
yarn add graphql
yarn add -D @graphql-codegen/cli
graphql-codegen init
を実行し、configのymlを生成
gqlgenで生成したschemaのパスを生成先として答えていますが、必要に応じてこのschemaを配置する場所を変更してください。今回はdefaultのままとします。
コマンド実行後にpacage.jsonに使用するpluginが追加されるので再度yarnします。
yarn graphql-codegen init
...
? What type of application are you building? Application built with React
? Where is your schema?: (path or url) ../server/graph/schema.graphqls
? Where are your operations and fragments?: src/**/*.graphql
? Pick plugins: TypeScript (required by other typescript plugins), TypeScript Operations (operations and fragments), Urql
Introspection (for Urql Client)
? Where to write the output: src/generated/graphql.ts
? Do you want to generate an introspection file? Yes
? How to name the config file? codegen.yml
? What script in package.json should run the codegen? codegen
Fetching latest versions of selected plugins...
Config file generated at codegen.yml
$ npm install
To install the plugins.
$ npm run codegen
To run GraphQL Code Generator.
✨ Done in 137.94s.
yarn
CLIで選択肢がなかったため手動でurql用のpluginを用意
yarn add -D @graphql-codegen/typescript-urql
pluginを追加した最終的なymlはこちら
overwrite: true
schema: "../server/graph/schema.graphqls"
documents: "src/**/*.graphql"
generates:
src/generated/graphql.ts:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-urql"
./graphql.schema.json:
plugins:
- "introspection"
適当な.graphqlを用意してgenerateしてみる
query findTodos {
todos {
text
done
}
}
yarn codegen
src/generated/graphql.ts
が生成されていたら準備は完了です。
ここまできたらようやくgraphQL clientの準備をします。
urqlのセットアップ
インストール
yarn add urql
Providerを用意
import React from "react"
import ReactDOM from "react-dom/client"
import App from "./App"
import "./index.css"
import * as Urql from "urql"
const client = Urql.createClient({
url: "http://localhost:8080/query",
})
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<Urql.Provider value={client}>
<App />
</Urql.Provider>
</React.StrictMode>
)
繋ぎこみ
App.tsx
で生成されたhookを使ってfetchできることを確認できたら完成です。
import * as React from "react"
import "./App.css"
import { useFindTodosQuery } from "./generated/graphql"
function App() {
const [result, reexecuteQuery] = useFindTodosQuery()
const { data, fetching, error } = result
if (fetching) {
return <p>loading...</p>
}
if (error) {
return <p>error</p>
}
return (
<div>
{data?.todos.map((v, i) => (
<p key={i}>{v.text}</p>
))}
</div>
)
}
export default App
おわり
queryだけですがReact + Go + GraphQL + urqlの最小構成を作ることができました。
Discussion