【Vue.js】データベースの代わりにGoogle SpreadSheetを使う

経緯

Vue.jsの習得がてら作成した所得税・住民税・事業税・国民健康保険計算シミュレーションは、現在「福岡県福岡市」の税率で計算します。
これを複数地域に対応したいのですが、各税率をどう持たせようか考えていました。せっかくVue.jsで完結してるのにサーバサイドを作るのはなんかテンション上がらないなぁと感じてまして。
JSONファイルをサーバに設置しダイレクトで使用するか?
いやいや、いちいちJSONに変換しないといけないのは辛いし、メンテナンス性も極悪。
FireBaseでも使ってみるかなぁとも考えましたが、リアルタイム性も特にいらないしメンテナンス画面どうするよ…とか考えてたところ、いっそGoogle SpreadSheetそのまま使えばよくね?ってなりました。

目的

以下の情報を保持・取得できればいいはず。

  • 住民税、国民健康保険料の税率
  • 都道府県と地方自治体の情報

福岡市の国保税率は以下のような感じ。都道府県と地方自治体の情報も正規化した構造にしてあるので似たようなもの。

ID 地域 種別 所得割税率 資産割税率 均等割 世帯割 上限額
1 福岡市 医療分 7.8 21,353 21,710 580,000
2 福岡市 支援分 2.97 7,870 8,002 190,000
3 福岡市 介護分 2.78 8,878 6,878 160,000

これがJSON形式で取得できれば良い。

Google SpreadSheetをAPIのように使う方法

GAS(Google Apps Script)を利用すると、Web APIが簡単に作れる模様ですね。以下記事を参考にさせていただきました。※基本的に丸コピーで動くので(ありがたや)、当記事ではソースは載せません。リンク先のQiitaの記事を直接見てください。
参考: Google SpreadSheet のデータを JSON 形式で取得する Web API をサクッと作る

注意点

上記記事を参考に作ってる際、最初以下のような形でスプレッドシートのIDを動的にパラメータから取得するようにしてましたが、これはまずかったです。idがばれたら公開したくないスプレッドシートも見られる可能性がでてきますね。不特定多数の人がアクセスできるようにするなら、スプレッドシートに紐づく形にした方が安全ですね。

// idはリクエストパラメータから取得...意図しないスプレッドシートが見られる懸念あり
const spreadsheet = SpreadsheetApp.openById(id).getSheetByName(sheetName);

↓ スプレッドシートに紐づくようにしておきます

const spreadsheet = SpreadsheetApp.getActive().getSheetByName(sheetName);

Vue.js側からGASのWebアプリケーションをコールする

定番のaxiosを使います。

まずはインストール

npm install axios

package.jsonはこんな感じ

{
  ...
  "dependencies": {
    "axios": "^0.18.0",
  ...

Vue.js側の実装

new Vue({
  el: '#app',
  data () {
    return {
      info: null
    }
  },
  mounted () {
    axios
      .get('WebアプリケーションURL')
      .then(response => (console.log(response.data)))
  }
})

CORSのエラーがでました。

Access to XMLHttpRequest at 'https://script.google.com/macros/s/{スプレッドシートのID}/exec?&sheetname={sheetName}' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

ドメイン違うので当然か…そもそもGASで作成したWeb APIはCORS対応しているのか?と一瞬不安がよぎりましたが対応してる模様。
なぜ動かないのか、、axiosに何かあるのかと調べたらcrossDomain:trueでいけました。ただ、このことについてaxiosのドキュメントないけど。。

new Vue({
  el: '#app',
  data () {
    return {
      info: null
    }
  },
  mounted () {
    axios
      .get('WebアプリケーションURL', { crossDomain: true })
      .then(response => (console.log(response.data)))
  }
})

スプレッドシートからGASを利用して、Web API経由で取得したJSONはこちら

[
  {
    "id": 1,
    "region_id": "福岡市",
    "kind": "医療分",
    "income_tax_rate": 7.8,
    "asset_rate": "",
    "equal_value": 21353,
    "household_value": 21710,
    "limit": 580000
  },
  {
    "id": 2,
    "region_id": "福岡市",
    "kind": "支援分",
    "income_tax_rate": 2.97,
    "asset_rate": "",
    "equal_value": 7870,
    "household_value": 8002,
    "limit": 190000
  },
  {
    "id": 3,
    "region_id": "福岡市",
    "kind": "介護分",
    "income_tax_rate": 2.78,
    "asset_rate": "",
    "equal_value": 8878,
    "household_value": 6878,
    "limit": 160000
  }
]

うん、希望通りの結果ですね。

※ 念のためレスポンスヘッダ確認すると、access-control-allow-origin: *になってますね。Google悪くない。

欠点

どうも、データが多くなりレスポンスに時間がかかるようになるとエラーになるっぽい。
データが増えたら別の手段に移行すればいいかな。。今は手軽さが一番!

まとめ

これでサーバサイドを構築する必要もないし、自分でJSONに成型する必要もないし、メンテナンス画面もいらないしで今のところ必要十分ですね。
ただ、レスポンスタイムがこれだけの量でも443msかかってるので、やはりパフォーマンスは見込めないですね(まあ当たり前ですか)
まあ、今回使用する部分は局所的ですし、都道府県・地方自治体を変更した場合に読み込みなおせばいいようにVuexにぶち込む予定なので、我慢できるかなぁ。