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

undefined

はじめに

Advent Calendar 2021のDay8。

今回はNuxt3とHasuraGraphQLをつなげるよう実装していきます。

GraphQLクライアントを選定する

Nuxt3だと対応していないライブラリが多いようなので、Nuxt3で利用できるGraphQLクライアントを選定します。

  • apollo-client
  • 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

NuxtWelcomeNuxtPageに変更しましょう

<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データベースをたてて、クラウドにデプロイできるよう準備をしていきます。


クラウドアンドビルド株式会社

Cloud and Build, Inc.

クラウドアンドビルド株式会社は、Google Cloud パートナー企業です。

GCP 導入から開発・コンサルティングまでワンストップでお任せください。

お問い合わせ

デジタル変革を開始しますか?

お気軽にお問い合わせください。

contact_at_cloudandbuild.jp
所在地

〒104-0061

東京都中央区銀座2-14-8

ML20191021

Designed by Freepik
© 2020 Cloud and Build, Inc.