GASでもできるけど、スプシをAPI化するサービスを作ってみた。そして、そのときハマったこと。

開発しているGoogleスプレッドシートのAPI化サービス「[SSSAPI](https://sssapi.app)」。
無事にオープンβ版を公開できたので、利用技術や開発時にハマった点をまとめてみました。
なにかの参考になれば。

https://sssapi.app

主な利用技術は、[Nuxt](https://ja.nuxtjs.org/)/[Firebase](https://firebase.google.com/)/[Cloud Run](https://cloud.google.com/run/?hl=ja)/[Cloudflare](https://www.cloudflare.com/ja-jp/)の4つです。

## SSSAPIとは？

GoogleスプレッドシートのURLを登録すると、
JSONを返してくれるAPIを作成してくれるWebサービスです。

たとえば、こんなスプレッドシートだったら、

![スクリーンショット 2021-08-25 23.53.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/87ca7f09-3308-44d0-9510-7be7c93fc58c.png)

こんな感じの結果を返してくれます。
1行目がフィールド名になり、同じフィールド名がある場合は配列で返します。

```sh
$ curl -s "https://api.sssapi.app/FbhGsWrdhVXR9iHzYSdMs" | jq
[
  { "id": 1, "name": "Alice", group: ["A", "B"] },
  { "id": 2, "name": "Bob",   group: ["A", "B"] },
  { "id": 3, "name": "Chris", group: ["C"] },
  { "id": 4, "name": "Dave",  group: ["D", "A"]},
  { "id": 5, "name": "Ellen", group: ["E", "A"]},
  { "id": 6, "name": "Frank", group: ["F"]}
]
```

他にも、1件取得やページング、フィルターにも対応してます。

#### 1件取得

```sh
# A列が2のリスト要素を取得
$ curl -s "https://api.sssapi.app/FbhGsWrdhVXR9iHzYSdMs/2" | jq
{ "id": 2, "name": "Bob",   group: ["A", "B"] }
```

#### ページング

```sh
# page/page_size style
$ curl -s "https://api.sssapi.app/FbhGsWrdhVXR9iHzYSdMs?page=2&page_size=2" | jq
[
  { "id": 3, "name": "Chris", group: ["C"] },
  { "id": 4, "name": "Dave",  group: ["D", "A"]}
]

# limit/offset style
$ curl -s "https://api.sssapi.app/FbhGsWrdhVXR9iHzYSdMs?limit=2&offset=2" | jq
[
  { "id": 3, "name": "Chris", group: ["C"] },
  { "id": 4, "name": "Dave",  group: ["D", "A"]}
]
```

#### 検索/フィルタ

```sh
# nameに"e"を含む検索
$ curl -s "https://api.sssapi.app/FbhGsWrdhVXR9iHzYSdMs?filter__name__contains=e" | jq
[
  { "id": 1, "name": "Alice", group: ["A", "B"] },
  { "id": 4, "name": "Dave",  group: ["D", "A"]},
  { "id": 5, "name": "Ellen", group: ["E", "A"]}
]

# 配列のgroupに"B"という要素が含む検索
$ curl -s "https://api.sssapi.app/FbhGsWrdhVXR9iHzYSdMs?filter__group__array_contains=B" | jq
[
  { "id": 1, "name": "Alice", group: ["A", "B"] },
  { "id": 2, "name": "Bob",   group: ["A", "B"] }
]
```


※ソートも要望をもらったので、近日対応予定です。

### アクセス制限も

SSSAPIでは、APIごとにアクセスできるドメインを設定できます。

![allow_domain.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/10474477-7c67-f848-2c8a-ad095a78dfa4.png)

この設定はクロスオリジン(Cross-Origin)からのアクセスだけでなく、
直接アクセスでも有効で、直接APIを叩いても制限できることができます。

![block_route.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/1e9f25a2-d589-4bd8-892e-dfd9bc30fe4f.png)

アクセストークンを利用した認証もできるので、
サーバ内など自分だけは直接アクセスしたい場合でもOK


## 利用技術とシステム構成

システム構成はこんな感じ。

![スクリーンショット 2021-08-26 0.47.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/c0f0b032-674b-bd08-cbd0-942f338482d3.png)

大きく3つに分けていて、以下のような感じに。

1. トップページやヘルプ
2. ログイン後の管理画面
3. APIサーバ

ハマった点にも書いていますが、
当初は1.と2.を一緒にしてNuxtのSPAにしてましたが、
やはりトップページが遅いのはダメだと思い、SSGで対応できるよう分割するように。

SSRも検討しましたけど、[Firebase](https://firebase.google.com/?hl=ja)や[Netlify](https://www.netlify.com/)も分けていたので、それを参考にしています。
<small>(ただ、取得したドメインが悪く、サブドメインにappを使えなかった。。)</small>

図には書いてなかったり、もろもろ使っている技術はこんな感じ。
UI系は[Buefy](https://buefy.org/documentation/field/)を使ってます。

#### Firebase Hostingをマルチサイトで利用

Firebase Hostingでは、複数のサイトを利用でき、 
1.と2.の2箇所でHostingを利用しています。
・[複数のサイトでプロジェクトのリソースを共有する  |  Firebase](https://firebase.google.com/docs/hosting/multisites?hl=ja)

トップページ側では、Firebase Analyticsを使っているので、
これで同じプロジェクトにイベントをまとめて確認できるように。

その時書いた記事はこちら。
・[Firebase Hostingで複数サイトをデプロイする - くらげになりたい。](https://www.memory-lovers.blog/entry/2021/06/29/135457)

#### トップ画面の画像はnuxt/imageで最適化

ハマった結果、トップページがSSGになったので、
[Nuxt/image](https://image.nuxtjs.org/)を使えるように！

画面サイズごとのリサイズやwebpへの変換などもだいぶ楽になりました。

その時書いた記事はこちら。
・[nuxt/imageで画像を最適化を試してみる - くらげになりたい。](https://www.memory-lovers.blog/entry/2021/07/02/133000)

#### nuxt/contentでヘルプページを楽に

トップページ側には、ヘルプや規約ページなど、
静的ファイルでよいものを集めています。

ただ、ヘルプなどでHTMLを書くの大変なので、
Markdownで書けるように、[nuxt/content](https://content.nuxtjs.org/ja)を利用。
これで、記事を書く感じで、ヘルプページも更新しやすく。

その時書いた記事はこちら。
・[@nuxt/contentでヘルプや使い方のページをサクッと作る - くらげになりたい。](https://www.memory-lovers.blog/entry/2021/08/24/150000)

#### デプロイの結果はSlackで通知

毎回デプロイされてるかをコンソールで見るのはつらいので、
Slackに通知される仕組みにしています。

Hostingへデプロイするときは、GitHubActionsのライブラリを使って通知。
・[rtCamp/action-slack-notify: GitHub Action for sending a notification to a Slack channel](https://github.com/rtCamp/action-slack-notify)

Cloud Buildは少しめんどくさいけど、
Cloud Functionsを使えば、通知できるのでFunctionを作成して利用。

その時書いた記事はこちら。
・[GitHub Actionsでデプロイの成功/失敗をSlackに通知する - くらげになりたい。](https://www.memory-lovers.blog/entry/2021/06/18/110000)
・[Cloud Buildの結果をCloud FunctionでSlackに通知する - くらげになりたい。](https://www.memory-lovers.blog/entry/2021/07/26/180000)

Cloud Buildを通知するCloud FunctionsもGitHubで公開しています。
・[memory-lovers/cloudbuild-slack: Slack Notifier for Cloud Build](https://github.com/memory-lovers/cloudbuild-slack)

アプリ側でもCloud Functions for Firebaseを使っている場合は、
デプロイ時に「該当の関数がないよ！」と、怒られてしまうので、
GCP Cloud Functionにデプロイする感じがよいです。


## ハマったところ

### SPAなのでトップページが遅い。。

上でも書いたとおり、当初はSPAだったので、トップページでも遅い。。
東京リージョンならまだいいかなと思ってたけど、やっぱり遅い。。

ただ、SSRは認証周りつらいこともあり、悩んでいたところ、
認証なし(=SSG)と認証あり(=SPA)に分離する形に落ち着きました。

マルチホスティングやnuxt/imageも使えたんで、だいぶ早く。

### PWAのキャッシュがうまく更新されない。。

力不足感も否めないですが、PWAのキャッシュがうまく使いこなせず。。

NetworkFirstを設定していても、

1. キャッシュを表示
2. 更新があるか確認
3. 更新があれば、キャッシュを更新
4. 再度開いたときに新しいバージョンが表示

という振る舞いになるよう。。？
・[Service Worker の更新](https://developers.google.com/web/fundamentals/primers/service-workers?hl=ja#update-a-service-worker)

バージョンをチェックして更新をうながす処理を入れる形でも、
トップページでそれが出るのもあまりなので、今回はPWAを利用しないように。

CloudflareのCDNにキャッシュされているので、それで十分な感じ。
(良い方法があれば、コメントください！)


### Google認証で権限を取得すると範囲が広すぎる

登録したスプレッドシートのURLを読み込んで、
JSON化するんですが、[Sheet API](https://developers.google.com/sheets/api)を使うために必要な権限が広い。。

競合のツールを見ても、Googleアカウントでログインするものが多いんですが、
・Google Driveの全権限が必要だったり、
・Google Spread Sheetsの権限が必要だったり
と、大事なファイルが入っている普段遣いのアカウントだとちょっと怖いなと。。

ログイン時だとどのファイルをAPIにするかわからないので、仕方ない部分もありますが、
SSSAPIでは、サービスアカウントを共有設定に追加してもらう形にしています。

これであれば、権限をもらう必要もなく、使うハードルを少し下げれたかなと。

## GASじゃだめだったのか？

ぼくも昔はこの形でAPI化してましたが、

- シートごとにGASの設定が必要でめんどうだったり、
- どれが設定したスプレッドシートか忘れてしまうので、

一元管理ができればな〜と思って、作りはじめました。

個人開発するのが好きなので、思いついたままに、
いろいろ作ろうとすると、一元管理できてるとだいぶ楽に。

とりあえず、SSSAPIを見れば、どこに置いたシートかもわかるので、
目的のシートを探す手間も省けます。

また、1件取得やページング、フィルタなどもやりたくなるけど、
GASをがんばって書かないといけないので、GASに不慣れな自分にはよいサービスに。

## SSSAPIでは、β版ユーザを募集中です！

最初は全件取得だけでしたが、フィルタやページングなどもできるようになり、
無事オープンβ版を公開できるようになりました(*´ω｀*)

β期間中はプレミアムプランの内容を無料で使えるのでお得！
ぜひぜひ、この機会にお試しいただければ〜！

GoogleスプレッドシートのAPI化サービス
『SSSAPI』
Twitter: [@sssapi_app](@sssapi_app)

https://sssapi.app

励みや記事を書くモチベにもなるので、
よかったら、LGTMやシェア、はてブなど、よろしくおねがいしますm(_ _)m

[はてなブックマーク](https://b.hatena.ne.jp/entry/s/qiita.com/kira_puka/items/b61f8cd682161afb55a9)

なんで作ったかの話はこちらに書いてるので、もしよければ!!

https://zenn.dev/kira_puka/articles/f9496a6a847799