[Advent Calendar 2021][Day8]Nuxt3とHasuraGraphQLをつなげる

はじめに
Advent Calendar 2021のDay8。
今回はNuxt3とHasuraGraphQLをつなげるよう実装していきます。
GraphQLクライアントを選定する
Nuxt3だと対応していないライブラリが多いようなので、Nuxt3で利用できるGraphQLクライアントを選定します。
- apollo-client
- 人気のあるライブラリです
- Nuxt3だとcjsの読み込みに失敗するのでNG
- issueがあがっています。まだ未対応かもです。
- graphql-request
- シンプルなGraphQLクライアント
- subscriptionには未対応
- urql
- シンプルなGraphQLクライアント
- ドキュメントキャッシュというキャッシュ機構がある
- GraphQL Code Generatorをサポートしている
urqlを使おうと思います。
Nuxt3でurqlを使っているGitHubのリポジトリが見つかったので、そちらを参考にさせていただきました。
参考
https://github.com/bicouy0/nuxt3-urql
実装する
- package.json
nuxt3-urqlのpackage.jsonを参考に依存関係を記述します。
{ "private": true, "scripts": { "dev": "nuxi dev", "build": "nuxi build", "start": "node .output/server/index.mjs", "lint": "eslint . --ext .js,.jsx,.ts,.tsx,.vue" }, "devDependencies": { "@graphql-codegen/cli": "^2.2.1", "@graphql-codegen/fragment-matcher": "^3.1.0", "@graphql-codegen/near-operation-file-preset": "^2.2.2", "@graphql-codegen/typed-document-node": "^2.2.1", "@graphql-codegen/typescript": "^2.4.1", "@graphql-codegen/typescript-apollo-client-helpers": "^2.1.8", "@graphql-codegen/typescript-operations": "^2.2.1", "@graphql-codegen/typescript-urql": "^3.4.1", "@graphql-codegen/typescript-urql-graphcache": "^2.2.2", "@graphql-codegen/typescript-vue-urql": "^2.2.1", "@graphql-codegen/urql-introspection": "^2.1.0", "@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/parser": "^5.4.0", "@urql/exchange-graphcache": "^4.3.5", "@urql/vue": "^0.6.0", "eslint": "^8.3.0", "eslint-plugin-vue": "^8.1.1", "graphql-tag": "^2.12.6", "nuxt3": "^3.0.0-27307420.6a25d3e", "typescript": "^4.5.2", "water.css": "^2.1.1" }, "dependencies": { "graphql": "^16.0.1" }}
- codegen.yml
こちらもnuxt3-urqlを参考につくります。schemaは、HasuraGraphQLのエンドポイントを指定します。
schema: http://localhost:8080/v1/graphqldocuments: ./gql/**/*.graphqlgenerates: gql/schema.ts: plugins: - typescript gql/: preset: near-operation-file presetConfig: baseTypesPath: schema.ts extension: .ts plugins: - typescript-operations - typed-document-node
- gql/queris/calendar.graphql
HasuraGraphQLを使って適当なクエリを作成します。
query MyQuery { calendars { id name participants { article_title article_url calendar_id created_at day uid updated_at } }}
- plugins/urql.ts
後ほど解説します。
import { createClient, ssrExchange, dedupExchange, fetchExchange, Client } from '@urql/core';import { defineNuxtPlugin } from '#app'import { ref } from "vue";const ssrKey = '__URQL_DATA__'export default defineNuxtPlugin(nuxt => { const { vueApp } = nuxt const ssr = ssrExchange({ isClient: process.client }) if (process.client) { nuxt.hook('app:created', () => { ssr.restoreData(nuxt.payload[ssrKey]) }) } if (process.server) { nuxt.hook('app:rendered', () => { nuxt.payload[ssrKey] = ssr.extractData() }) } const client = createClient({ url: 'http://localhost:8080/v1/graphql', exchanges: [ dedupExchange, ssr, fetchExchange, ] }) nuxt.provide('urql', client) vueApp.provide('$urql', ref(client))})declare module '#app' { interface NuxtApp { $urql: Client }}
- app.vue
NuxtWelcome
をNuxtPage
に変更しましょう
<template> <div> <NuxtPage /> </div></template>
- pages/index.vue
とりあえずGrahpQLクエリを試すだけの実装です。
<template> <section> <div v-if="data"> <table v-if="data.calendars.length"> <tbody> <tr v-for="c in data.calendars" :key="c.id"> <td>{{ c.name }}</td> </tr> </tbody> </table> <div v-else> <em>no results</em> </div> </div> </section></template><script setup lang="ts">import { MyQueryDocument } from "~/gql/queries/calendar";import { useClientHandle } from "@urql/vue";const urql = useClientHandle();const { data, fetching } = await urql.useQuery({ query: MyQueryDocument })</script>
とりあえず、Hasuraとつなげることに集中して、諸々、GraphQLエンドポイントをハードコードしている部分などは、別途リファクタして改善していきたいと思います。
理解する
Nuxt3のPlugin機構がNuxt2から異なっているので、確認していきましょう。
Nuxt3 plugin
defineNuxtPlugin を使って、nuxtAppにプラグインを追加することができます。
参考
https://v3.nuxtjs.org/docs/directory-structure/plugins/
NuxtApp
nuxtApp.provide
で各コンポーネントでnuxtAppを通じてアクセスできるようになっています。
参考
https://v3.nuxtjs.org/docs/usage/nuxt-app/
urql Server-side Rendering
SSR Exchangeという機能を利用してServer側でレンダリングされたときの、GraphQLクエリ結果をクライアント側で取り出すことができるようです。
- サーバー側では
app:rendered
でデータ抽出 - クライアント側では
app:created
でデータ復元
参考
https://formidable.com/open-source/urql/docs/advanced/server-side-rendering/#vue-suspense
urql vue useClientHandle
useClientHandle
を使ってpluginで定義したurqlクライアントを取得できます。
参考
https://formidable.com/open-source/urql/docs/api/vue/#useclienthandle
まとめ
Nuxt3は、まだ静的サイトの対応が実装されていないので今回はSSRで作っていくことにしました。 SSRでpluginの機構がどのように動くかは、少し複雑で考えることが多いですね。 個人的には、要件次第ですが、静的サイトにしてJAM Stackのほうがシンプルで好みです。SSRを使うとしたら動的にレンダリングするような、サイトを構築する場合ですね。
次回は、CloudSQLでPostgreSQLデータベースをたてて、クラウドにデプロイできるよう準備をしていきます。
