NOWでデプロイできるGoogleフォームみたいに追記できるAPIを作ったので、GitHubに公開してみた

[Spread Sheet API](https://developers.google.com/sheets/api/)を見てたら、Googleフォームみたいに空いている行に追記できるらしい...
便利そうなので、汎用的に使えるようにNOW APIとしてデプロイできるようにしてみた＆GitHubにも公開してみた。


### 作ったもの

GitHub: [Append Row API using ZEINT NOW and Spread Sheet API](https://github.com/memory-lovers/append-row-api_zeit-now)

こんな感じで、API叩くとスプレッドシートに追記できる。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/b3e01a00-c186-30fa-92ca-a2450240c40f.gif" width="600" />



### 使い方: デプロイする

#### 1. git clone

まずはgit clone

```console
$ git clone https://github.com/memory-lovers/append-row-api_zeit-now.git
```

#### 2. サービスアカウントのキーファイルの配置

`credential.json`というファイル名で、認証情報のキーファイルを配置

#### 3. 追記したいスプレッドシートの権限設定

そのままだとサービスアカウントに書き込み権限がないためエラーに...
そのため、追記したいスプレッドシートの共有権限にサービスアカウントを追加が必要。

サービスアカウントの作成やスプレッドシートの共有設定は、
以下の記事がわかりやすかった...(*´ω｀*)
「[Node.jsでGoogleスプレッドシートを操作する - LCL Engineers' Blog](https://techblog.lclco.com/entry/2018/11/30/120000)」

#### 3. ローカルで試す

`now dev`コマンドでローカルで動かすことができます。
実行すると`http://localhost:5001`で起動します。

```console
$ now dev -p 5001
// or
$ npm run dev
```

##### nowコマンドやアカウントがない場合...

[こちらのZEITのページ](https://zeit.co)からログイン&アカウント作成!

[公式ドキュメント](https://zeit.co/docs/)にあるように、インストールとログイン!

```console
# nowコマンドのインストール
$ npm i -g now

# CLIでのログイン
$ now login
```

#### 3. ZEIT now にデプロイ

`now`コマンドでデプロイできます。
プロジェクト名は、`now.json`の`name`に書いてある**append-api**になります。

```console
$ now
// or
$ npm run deploy
```

### 使い方: API の呼び出し

デプロイした API は、以下のパラメタを受け取ります

1. 追記するシートの ID: `spreadsheetId`
2. 追記する内容の配列: `values`

curl で呼び出すサンプルは以下のとおりです。
URL には、`https://append-api.memory-lovers.now.sh`
のようなデプロイした URL を設定。

ローカルで起動した場合は、`https://localhost:5001`を設定。

```bash
#!/bin/bash

SHEET_ID='YOUR_SHEET_ID'
URL='API_URL'

curl -i \
-H "Accept: application/json" \
-H "Content-Type:application/json" \
-X POST --data '{ "spreadsheetId": "'$SHEET_ID'", "values": [ ["A", "B", "C"], ["D", "E", "F"] ]  }' \
"$URL/append"
```


------

### コードはこんな感じ。
主にExpressに関する処理が多いですが、Google APIsを使うのは、
`const doAppend = async (spreadsheetId, values) => {`のあたりに集約。


```javascript
import bodyParser from "body-parser";
import Express from "express";
import { google } from "googleapis";
require("./credential.json"); // サービスアカウントの認証情報
const app = Express();

// POSTのBODYにJSONを使うため、body-parserを有効化
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

/**
 * Spread Sheetに行を追加する処理
 * @param {String} spreadsheetId シートID
 * @param {String[][]} values 追記するデータ。2次元配列で指定
 */
const doAppend = async (spreadsheetId, values) => {
  // パラメタのチェック
  if (!spreadsheetId || !values) throw new Error("Error: Invalid Params");

  // Spread Sheet APIを使うための認証処理
  const auth = await google.auth.getClient({
    scopes: ["https://www.googleapis.com/auth/spreadsheets"]
  });
  const sheets = google.sheets({ version: "v4", auth });

  // APIを呼び出して、行の追加処理
  const req = {
    // シートのID
    spreadsheetId: spreadsheetId,
    // A1に追記することを指定
    range: "A1",
    // 追記する形式を指定。
    valueInputOption: "USER_ENTERED",
    // A1に値があったら下方向に空欄を探しにいく
    insertDataOption: "INSERT_ROWS",
    // 追加する行のデータ。2次元配列で指定
    resource: {
      values: values
    }
  };
  await sheets.spreadsheets.values.append(req);
};

// '/append'にアクセスしたら、doAppend関数を呼ぶようにマッピング
app.post("/append", async (req, res) => {
  try {
    // パラメタのチェック
    if (!req.body) throw new Error("Error: Empty Body");

    // パラメタの取得
    const spreadsheetId = req.body.spreadsheetId || "";
    const values = req.body.values || "";

    // 追記処理の呼び出し
    await doAppend(spreadsheetId, values);

    res.end();
  } catch (error) {
    console.error(`Error in append: ${error}`, error);
    res.status(500).send({ error: `${error}` });
  }
});
export default app;
```

##### はまったところ...`credential.json`を認識しない...

now.jsonに環境変数`GOOGLE_APPLICATION_CREDENTIALS`を設定して、
読み込むファイルを指定していたけど、エラーが...

```json
"env": {
  "GOOGLE_APPLICATION_CREDENTIALS": "./credential.json"
}
```

> Error in append: Error: The file at ./credential.json does not exist, or it is not a file. 

デプロイされたフォルダを見てみると、`credential.json`が配置されていない...

いろいろ調べてみたところ、ビルドをするので関連のないファイルは配置されないっぽい...
なので、`index.js`の冒頭に以下を追加して、読み込むように変更してみたところ、
うまく認識されるようになったヽ(=´▽`=)ﾉ

```javascript
require("./credential.json"); // サービスアカウントの認証情報
```

### 活用事例1: 問い合わせ管理

開発しているWebサービスの問い合わせ効率化のために利用(*´ω｀*)

Nuxtに用意したフォームで受けた内容をスプレッドシートに転記して管理できるように♪

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">書籍のリクエスト、できてきた(*´ω｀*)<br><br>ピタゴラスイッチ的にFirestoreトリガーで、<br>Slackに通知とSpread Sheetに追記ヽ(=´▽`=)ﾉ<br><br>これで、だいぶ楽になるはず(*´ω｀*)♪ <a href="https://t.co/9SIY2T23u3">pic.twitter.com/9SIY2T23u3</a></p>&mdash; 積読ハウマッチ📚きらぷか (@kira_puka) <a href="https://twitter.com/kira_puka/status/1172029880680079360?ref_src=twsrc%5Etfw">September 12, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

以前、[書いた記事](https://qiita.com/kira_puka/items/d372cf8ce1c5c98360bd)のSlackAPIも使い、
通知と管理を一度にできるようになりましたヽ(=´▽`=)ﾉ


### 活用事例2: 統計情報の収集

定期的にユーザ数やデータ数などを集計してスプレッドシートに追記できるように！
スプレッドシートに追記するとグラフを出せるようになるのですてき(*´ω｀*)

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">SpreadSheetAPIとなかよくなれたので<br>とりあえず、取ってる統計データを入れてみた(*´ω｀*)<br><br>ここ1週間で、1,300万円分が積まれ、<br>総額2,000万円近く、登録されてた( ﾟдﾟ)!<br><br>強者達が...集まってきている...((((；ﾟДﾟ))))ｶﾞｸｶﾞｸﾌﾞﾙﾌﾞﾙ<a href="https://twitter.com/hashtag/%E7%A9%8D%E8%AA%AD%E3%83%8F%E3%82%A6%E3%83%9E%E3%83%83%E3%83%81?src=hash&amp;ref_src=twsrc%5Etfw">#積読ハウマッチ</a> <a href="https://t.co/esCqTrQmLK">pic.twitter.com/esCqTrQmLK</a></p>&mdash; 積読ハウマッチ📚きらぷか (@kira_puka) <a href="https://twitter.com/kira_puka/status/1171674794653188096?ref_src=twsrc%5Etfw">September 11, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

個人開発なので常に稼働が足りないですが、
こういった裏方作業的なのも、もっと効率化していけるようになりたい...!!

以上!!

## こんなのつくってます！！

最近、積読用の読書管理アプリ「積読ハウマッチ」をリリースしました！
[積読ハウマッチ](https://tsundoku.site)は、Nuxt.js+Firebaseで開発してます!

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/572d4947-f40b-e4dc-1c9c-bc584cd2a66c.png" width="200"/>

もしよかったら、遊んでみてくださいヽ(=´▽`=)ﾉ

要望・感想・アドバイスなどあれば、
公式アカウント([@MemoryLoverz](https://twitter.com/MemoryLoverz))や開発者([@kira_puka](https://twitter.com/kira_puka))まで


# 参考にしたサイト様
- [スプレッドシート API で行を追加する - Qiita](https://qiita.com/castaneai/items/a53d0b89bca5b84654be)
- [Googleスプレッドシートをプログラムから操作 - Qiita](https://qiita.com/howdy39/items/ca719537bba676dce1cf)
- [Google spreadsheetの値を取得する - Qiita](https://qiita.com/suisuina/items/a41932088acacea4835e)
- [API キーの使用  |  認証  |  Google Cloud](https://cloud.google.com/docs/authentication/api-keys?rd=1&visit_id=637037608757242156-1685701782)
- [googleapis/google-api-nodejs-client: Google's officially supported Node.js client library for accessing Google APIs. Support for authorization and authentication with OAuth 2.0, API Keys and JWT (Service Tokens) is included.](https://github.com/googleapis/google-api-nodejs-client#using-api-keys)
- [Google APIs Node.js Client を使って Google Analytics のページビューを取得する ｜ DevelopersIO](https://dev.classmethod.jp/server-side/node-js-server-side/using-google-apis-node-js-client/)
- [Storing complex secrets · Issue #749 · zeit/now](https://github.com/zeit/now/issues/749)
- [Storing authentication credentials file · ZEIT](https://spectrum.chat/zeit/now/storing-authentication-credentials-file~aee6b468-9ea0-4f2d-9b7a-f07831df2fd9)
- [ZeitのNowにデプロイするファイルを指定する時の注意 - Crieit](https://crieit.net/posts/Zeit-Now)
- [Nowを使って静的サイトとAPIを単一レポジトリで運用する](https://blog.hokuma.net/serverless/deploy_to_now/)
- [curlチートシート - Qiita](https://qiita.com/miminashi/items/f48cd4c25b06fcab5ea3)