@memo

ゆるくインプット、ゆるくアウトプット

SpreadsheetをDBとして使うぞ大作戦 on Nuxt

こんにちは。asatoです☀

いま面白いTwitterスペースと出会えるプロダクトspaces.bzを開発しています。

このプロダクトはDBが必要なのですが、できれば簡単でお金のかからないDBがほしい...そうだ!スプレッドシートをデータベースとして使いたい! ということで、Nuxt + Spreadsheet構成でプロダクトを開発してみたので、体験記をまとめたいと思います!

この記事でやること

スプレッドシートからデータを取得して表示するNuxtアプリを作ります。 サンプルのスプレッドシートは以下のようなイメージ。

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

スプレッドシートJSONで返却するGASを作成

まずはスプレッドシートの情報を取得するAPIを作っていきます。 GASでプログラムを組んでWebアプリとして公開する、をします。GASの方から!

function doGet() {
  const users = getUsers()

  return ContentService
    .createTextOutput(JSON.stringify(users))
    .setMimeType(ContentService.MimeType.JSON)
}

function getUsers() {
  const ss = SpreadsheetApp.openById('スプレッドシートのID')
  const sheet = ss.getSheetByName('シート名')
  const rows = sheet.getDataRange().getValues()
  const keys = rows.splice(0,1)[0]

  const users = rows.map((row) => {
    const object = {}
    row.map((value, index) => {
      object[keys[index]] = value
    })
    return object
  })

  return users
}

ちょこっとコードをみてみます。

doGet関数

このコードの最初の肝はdoGet関数を用いていることです。 この後Webアプリで公開すると話しましたが、doGet関数はWebアプリで公開したURLにGETリクエストが来たときに動く関数です。

今回のコードではdoGet関数が動き出したらgetUsers関数を呼び出してスプレッドシートの中身をJSONに整形し、

return ContentService
  .createTextOutput(JSON.stringify(users))
  .setMimeType(ContentService.MimeType.JSON)

で、JSONを返却しています。

getUser関数

次に、getUser関数をみてみます。

const ss = SpreadsheetApp.openById('スプレッドシートのID')

最初に対象のスプレッドシートを読み込んでいます。スプレッドシートのIDはスプレッドシートのURLからわかり、https://docs.google.com/spreadsheets/d/*****/edit*****の部分です。

const sheet = ss.getSheetByName('シート名')

次に、そのスプレッドシートのシート名のシートを持ってきます。

const rows = sheet.getDataRange().getValues()

そして、そのシートのデータが入力されているセルのデータを行単位の配列で取得します。 サンプルの例だと、rowsの値は以下のようになっています。

rows = [
  ['id', 'name', 'age', 'email'],
  ['1', 'Test A', '20', 'test_a@sample.com'],
  ['2', 'Test B', '30', 'test_b@sample.com'],
  ['3', 'Test C', '40', 'test_c@sample.com']
]
const keys = rows.splice(0,1)[0]

お次はrowsの1つ目の配列を取り出してkeysに代入しています。これによって、keys、rowsは以下のようになります。

keys = ['id', 'name', 'age', 'email']

rows = [
  ['1', 'Test A', '20', 'test_a@sample.com'],
  ['2', 'Test B', '30', 'test_b@sample.com'],
  ['3', 'Test C', '40', 'test_c@sample.com']
]

これでkeysjsonkeyになり、rowsに残った項目がvalueになる未来が見えてきました。

const users = rows.map((row) => {
  const object = {}
  row.map((value, index) => {
    object[keys[index]] = value
  })
  return object
})

そんな未来を実現するコードがこれです。 rows.map((row) => { 処理 })rowsから配列を一つずつ持ってきて変数rowに格納し処理を実行しています。 処理の部分でもrow.map((value, index) => { 処理2 })としてrowの要素を一つずつ取り出し、 処理2でobject[keys[index]] = valueとすることで各配列をオブジェクトの形に整形しています。

最終的にusersの値はこんな感じです。

users = [
  {
    id: 1,
    name: 'Test A',
    age: 20,
    email: 'test_a@sample.com'
  },
  {
    id: 2,
    name: 'Test B',
    age: 30,
    email: 'test_b@sample.com'
  },
  {
    id: 3,
    name: 'Test C',
    age: 40,
    email: 'test_c@sample.com'
  }
]

これを最後にreturnする関数になってます。

これでWebアプリを公開したときにスプレッドシートのシートをjsonで返却するGASの準備ができたので、次はWebアプリを公開していきます。

Webアプリを公開する

WebサイトはGASのエディターの上部にある「デプロイ」ボタンから行います。

f:id:at946:20220201095829p:plain

「デプロイ」>「新しいデプロイ」を選択して...

f:id:at946:20220201095907p:plain

「種類の選択」>「ウェブアプリ」を選択して...

f:id:at946:20220201095929p:plain

「新しい説明文」にわかりやすい名前(productionとかtestとか)、「次のユーザーとして実行」に「自分」、「アクセスできるユーザー」に「全員」を設定し、「デプロイ」を選択して...

f:id:at946:20220201095959p:plain

(必要な場合は)「アクセスを承認」を選択して...

f:id:at946:20220201100017p:plain

Google認証」して...

f:id:at946:20220201100113p:plain

「Advanced」を選択して、「<プロジェクト名>(unsafe)」を選択して...

f:id:at946:20220201100130p:plain

「Allow」して...

f:id:at946:20220201100153p:plain

完了!!🎉 「ウェブアプリ」の方に書いてあるURLがAPI(サイトとしても)公開しているURLになります。ちょっとcurlしてみましょう。

$ curl -L <URL>

[
  {"id":1,"name":"Test A","age":20,"email":"test_a@sample.com"},
  {"id":2,"name":"Test B","age":30,"email":"test_b@sample.com"},
  {"id":3,"name":"Test C","age":40,"email":"test_c@sample.com"}
]

返却されてるー!!

画面じゃないとわかりにくいなと思い、画像多めでお届けしました。 ってことなので、これをNuxtでの表示を作っていきます!

NuxtでAPI叩いて表示

最後に、今公開したAPIをNuxtアプリから叩いてデータを表示していきます。

やることは

  • 外部のAPIを叩くためにaxiosを導入する
  • CORS対応のためにserverMiddleware経由でGAS APIを叩く
  • これらを使ってデータを取得して表示する

です!

まず、@nuxtjs/axiosを導入してきます。

$ yarn add @nuxtjs/axios

モジュールを登録します。

# nuxt.config.js
  export default {
    ...
    modules: [
+     '@nuxtjs/axios'
    ],
    ...
  }

次に、serverMiddlewareを作成して経由して叩くことを考えます。

$ yarn add express
$ 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(<ウェブアプリ(GAS API)のURL>)
  res.send(users.data)
})

module.exports = {
  path: '/api/',
  handler: app,
}

nuxt.config.jsにも登録しておきましょう。

# nuxt.config.js
  export default {
    ...
+   serverMiddleware: [
+     '@/api/'
+   ],
    ...
  }

これで内部の/api/を経由してGAS APIからデータを取得することができるようになりました。

ウェブアプリのAPIを叩く準備が整ったので、ページを作成していきます。

# pages/index.vue
<template>
  <div>
    <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>
export default {
  data() {
    return {
      users: []
    }
  },
  async fetch() {
    this.users = await this.$axios.$get('/api')
  },
}
</script>

ちょっと長いですが、templatetablev-forで表形式に表示しているだけで、scriptaxiosで取得したユーザー情報をthis.usersに格納しているだけです!シンプル!

これでページにアクセスしてみると...

f:id:at946:20220201151108p:plain

データ取得成功!

スプレッドシートと同じ情報を表示できていますね!!🎉🎉🎉

まとめ

今回はスプレッドシートをDB代わりに使えないかなー、ということで、

をやってみました! それぞれいろいろな参考記事を書いてくださっている方々がいらっしゃったんですが、あわせ技のところで困ったりしたので一連通してやるとこうなるぞという記事を書いてみました。 誰かの参考になれば嬉しいです。

spaces.bzも応援よろしくおねがいします!