2021年10月1日

NuxtでスプレッドシートをDB代わりに使うぞ大作戦 Part3 - asyncDataでだって使いたい!

読了時間の目安: 4分


シリーズ最終章です。

[前回の記事]

NuxtでGoogle SpreadsheetをDB代わりに使うぞ大作戦 | at-blog

[前々回の記事]

NuxtでスプレッドシートをDB代わりに使うぞ大作戦 Part2 - モバイルだって使いたい! | at-blog

Part2では、PCは表示できるけどモバイルはできない、というPart1の課題をJSON→JSONPに更新することで解決しました🎉。

これでOKかと思いきや、spaces.bzの開発では次なる課題が現れました。

  • 動的OGPやりたいよね
  • Search Consoleでソフト404って出てるけどなんだろうね

うむ。いまのデータはcreatedで取得しているのですが、ロボットに情報を確認してもらうためにはasyncDataでデータを取得しないとですね。

たとえば、spaces.bzのデイリーランキングページを見てください。 このページでは動的OGPやソフト404対策としてアクセス時はasyncDataを使ってスプレッドシートからデータを取得しています。 また、日付の変更の場合はmethodsの関数を使ってスプレッドシートからデータを取得しています。

Part2の内容を踏まえると、asyncDataでも同じようにデータを取得しようとすれば実現できそうですよね。 (ボタンを押したらデータを更新する感じにします)

pages/index.vue
<template>
<div>
<button @click="getUsers">更新</button>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>email</th>
</tr>
</thead>
<tbody>
<tr v-for='user in users' :key='user.id'>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.age }}</td>
<td>{{ user.email }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
const jsonpAdapter = require('axios-jsonp')
export default {
async asyncData({ $axios }) {
const users = await $axios.$get('/api', { adapter: jsonpAdapter })
return {
users: users
}
},
data() {
return {
users: []
}
},
created() {
this.getUsers()
},
methods: {
async getUsers() {
this.users = await this.$axios.$get('/api', { adapter: jsonpAdapter })
}
}
}
</script>

よし、これで行けるはず。

document is not found

まあ、これで大丈夫なら記事書かないんですよね。

ということで、今回はasyncDataでもスプレッドシートをDB代わりに使おう大作戦です。

何がだめなの

なにが起きてるかというと、axios-jsonpでエラーが発生しています。 「document is not found」とか「window is not found」とか、このエラーはフロントエンドで動作する前提のパッケージがバックエンドで動作するときに起こります。 つまり、asyncDataはバックエンドでデータを取得するので、フロントエンドで動作する前提のaxios-jsonpは利用できないのです。

じゃあどうする

こういうパッケージはnuxt.config.jsssr: falseする、<no-ssr><client-only>タグで囲む、などの解決策が提示されています。 しかし、今回はasyncDataで動作させたいので採用できません。 かといって、何も考えずにJSONPをやめてしまえばまたモバイルで使えなくなる...。

ということでserverMiddlewareを使ってみましょう。 いままで「フロントエンド」→「GASのWebApp」と通信していた間にBFFのようにserverMiddlewareが入ります。 これによって、フロントエンドとしては同じドメインのサーバーと通信しているようになるのでCORS対策もバッチリになります。

モバイルで使えないってなったとき、JSONPじゃなくてserverMiddlewareでもよかったのですが当時は気づきませんでした。

NuxtアプリでserverMiddleware経由でGAS APIをコールする

まずはBFFでGAS APIをコールするコードを書いていきましょう。 ここでは express を利用します。

Terminal window
$ yarn add express

新しくapiディレクトリを作成し、その中にBFFのコードを書いていきましょう。

Terminal window
$ mkdir api
$ touch api/index.js
api/index.js
const express = require('express')
const axios = require('axios')
const app = express()
app.get('/', async (req, res) => {
const users = await axios.get(<ウェブアプリのURL(GAS API)>)
res.send(users.data)
})
module.exports = {
path: "/api/",
handler: app
}

今までpages/index.vueでやっていたaxiosの処理を転記した感じですね。 これをnuxt.config.jsserverMiddlewareとして登録しましょう(ついでにproxyも不要になったので消します)。

nuxt.config.js
export default {
...
axios: {
proxy: true
},
proxy: {
'/api': {
target: <ウェブアプリ(GAS API)のURL>,
pathRewrite: { '/api': '' }
}
},
serverMiddleware: [
'@/api/'
],
...
}

これでserverMiddlewareの準備は完了です。

フロントエンドもJSONPからJSONを取得するように更新しておきましょう。

pages/index.vue
...
<script>
const jsonpAdapter = require('axios-jsonp')
export default {
async asyncData({ $axios }) {
const users = await $axios.$get('/api', { adapter: jsonpAdapter })
const users = await $axios.$get('/api')
return {
users: users
}
},
methods: {
async getUsers() {
this.users = await this.$axios.$get('/api', { adapter: jsonpAdapter })
this.users = await this.$axios.$get('/api')
}
}
}
</script>

axios-jsonpも不要になったので、パッケージを削除しておきましょう。

Terminal window
$ yarn remove axios-jsonp

見ての通り、JSONPではなくJSONを期待してのコールですので、GASのコードを元(Part1)時点に戻していきます。

GAS APIをJSON返却に戻す

コード.js
function doGet(e) {
const users = getUsers()
const callback = e.parameter.callback
return ContentService
.createTextOutput(`${callback}(${JSON.stringify(users)})`)
.setMimeType(ContentService.MimeType.JAVASCRIPT)
.createTextOutput(JSON.stringify(users))
.setMimeType(ContentService.MimeType.JSON)
}
...

ここまででBFFの準備ができました🎉。

動作確認

最後に動作確認をしておきましょう。 期待動作は以下の通りです。

  1. http://localhost:3000/にアクセス → ユーザーA/B/Cのデータが表示される
  2. SpreadsheetにユーザーDのデータを登録
  3. Nuxtの方で「更新」ボタンを押す → ユーザーDのデータも表示される

ではやっていきましょう。

1. http://localhost:3000/にアクセス

http://localhost:3000/にアクセスして、ユーザーA/B/Cのデータが表示されることを確認!

表示されてる。

2. スプレッドシートにユーザーDを追加

idnameageemail
1Test A20test_a@sample.com
2Test B30test_b@sample.com
3Test C40test_c@sample.com
4Test D50test_d@sample.com

3. 更新ボタンを押下

http://localhost:3000/で更新ボタンを押して、ユーザーA/B/C/Dのデータが表示されることを確認! ユーザーDの情報が新たに表示されてる。

テスト成功🎊。

まとめ

今回はserverMiddlewareを使って、スプレッドシートをDB代わりに使ってみました。 ここまで色々と試行錯誤をしてきましたが、今回の方法はやりたいことができる中で一番手っ取り早い方法でしょう。 Google Sheet API とかも試みてみましたが挫折しました...

スプレッドシートだとデータも見やすいですし、GASで色々操作できたりもするので、作りたいプロダクトによっては1つの選択肢になってきそうです。 この記事が、どなたかの役に立てば嬉しいです。

  • 名前:asato
  • 仕事:スクラムマスター
  • 好き:家族、温泉、旅行、謎解き
  • 苦手:はじめまして、あんこ、うなぎ