GASによるWeb APIの作り方

# はじめに

Google Apps Script (GAS) のWebアプリをデプロイする機能を利用すると Web API を作ることができます。

Web API を利用すればスプレッドシートのデータを外部ページから参照・更新するなど、Google Workspaceアプリの利用幅を広げることができます。


# Web APIの作り方

## シンプルなAPIを作る

まずはJSON文字列を返すだけのシンプルなWeb APIから作ってみましょう。

1. スクリプトプロジェクトを作成する
    任意のフォルダにWebアプリとして公開するGASのスクリプトプロジェクトを作成します。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/fe5ca84d-4b4b-b4ba-689b-aae12da5ca3f.png)
    <br>

1. スクリプトを編集する
    GASでWebアプリを作成するにはGETリクエストに応答する doGet関数 かPOSTリクエストに応答する doPost関数 を定義する必要があります。
    以下はGETリクエストに応じて固定された文字列を返すシンプルなWeb APIの例です。
    ```javascript
    function doGet(e) {
      let data = {author: "__SATO__"};
      let json = JSON.stringify(data); // ※1
      let response = ContentService.createTextOutput(json); // ※2
      response.setMimeType(ContentService.MimeType.JSON); // ※3
      return response;
    }
    ```
    ※1 WebAPIは文字列を返す必要があるのでJSONオブジェクトは文字列化します。
    ※2 Webアプリとして公開できるようTextOutputオブジェクトに変換します。
    ※3 [レスポンスデータの種類](https://developers.google.com/apps-script/reference/content/mime-type?hl=ja#properties)を指定します。
    <br>

1. APIをデプロイする
    スクリプトエディタで「デプロイ」→「新しいデプロイ」と選択し、デプロイの種類を「ウェブアプリ」にしてから「デプロイ」ボタンをクリックしてください。
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/0d10944e-a728-ac67-1a33-9e63dfe24be9.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/70dca5c0-55de-06c6-4ddd-07df993d4957.png)
    <br>

1. 動作を確認する
    デプロイが完了すると下図のようにウェブアプリのURLが表示されるのでブラウザでアクセスしてみましょう。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/adfe3d4a-f053-1f51-a533-a8c114588e1c.png)

    すると下図のようにJSON文字列が表示されるはずです。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/7f6f2ab2-e821-312c-aa3c-3e26545114e4.png)


## GoogleWorkspaceアプリと連動させる

doGet関数 や doPost関数 の中は AppsScriptサーバ環境 なので、GoogleWorkspaceアプリと連携する処理は普段のGASの開発と同じ方法で記述できます。

ここでは試しに自分自身にGmailで通知を出すAPIを作ってみましょう。

1. スクリプトプロジェクトを作成する
    任意のフォルダにWebアプリとして公開するGASのスクリプトプロジェクトを作成します。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/fe5ca84d-4b4b-b4ba-689b-aae12da5ca3f.png)
    <br>

1. スクリプトを編集する
    GETリクエストに応答して固定された内容のメールを送信する doGet関数 を定義します。

    ```javascript
    function doGet(e) {
      // Gmailで通知を出す
      let recipient = Session.getActiveUser().getEmail();
      let subject = "GETリクエストを受け付けました"
      let timestamp = Utilities.formatDate(
          new Date(), Session.getScriptTimeZone(), "yyyy-MM-dd HH:mm:ss"
      );
      let body = `${timestamp}にGETリクエストを処理しました`;
      GmailApp.sendEmail(recipient, subject, body);
    
      // レスポンスを返す
      let response = ContentService.createTextOutput(body);
      response.setMimeType(ContentService.MimeType.TEXT);
      return response;
    }
    ```
    <br>

1. APIをデプロイする
    スクリプトエディタで「デプロイ」→「新しいデプロイ」と選択し、デプロイの種類を「ウェブアプリ」にしてから「デプロイ」ボタンをクリックしてください。
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/0d10944e-a728-ac67-1a33-9e63dfe24be9.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/70dca5c0-55de-06c6-4ddd-07df993d4957.png)
    <br>

1. 動作を確認する
    デプロイが完了すると下図のようにウェブアプリのURLが表示されるのでブラウザでアクセスしてみましょう。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/adfe3d4a-f053-1f51-a533-a8c114588e1c.png)

    すると下図のようにブラウザに文字列が表示され、Gmailに通知が届いているはずです。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/8ade97cb-6641-8b14-d2d8-3d2d22ca5237.png)


## パラメータを受け取る

ここまではHTTPリクエストに対して固定的された応答を返していましたが、実際のAPIはユーザ入力に応じて動作が異なる場合の方が多いと思います。

そこで、ここからはユーザ入力を受け取って処理する方法を少し紹介していきます。

### パスパラメータの処理

GASで作成したWebアプリのULRは基本 "exec" (※テストの場合は"dev") で終わりますが、GASのWebアプリはその後に "/hoge/huga" のようなパスを追加することもでき、"exec"ないし"dev"以降のパスパラメータは doGet関数や doPost関数 の引数として与えられるイベントオブジェクトの pathInfoプロパティ を参照することで取得できます。

以下はGETリクエストのパスパラメータに応じて挨拶を返すAPIのサンプルです。

```javascript
function doGet(e) {
  let pathInfo = new String(e.pathInfo); // "greeting/morning"などの形式
  let pathList = pathInfo.split('/'); // ["greeting", "morning"]に変換
  let response = null;
  if (pathList[0] == 'greeting' && pathList[1] == 'morning') {
    response = ContentService.createTextOutput('おはようございます！');
  } else if (pathList[0] == 'greeting' && pathList[1] == 'afternoon') {
    response = ContentService.createTextOutput('こんにちは！');
  } else if (pathList[0] == 'greeting' && pathList[1] == 'evening') {
    response = ContentService.createTextOutput('こんばんは！');
  } else {
    response = ContentService.createTextOutput('ハロー！');
  }
  response.setMimeType(ContentService.MimeType.TEXT);
  return response;
}
```

上記のAPIがデプロイできたら、デプロイURLの末尾にパスパラメータを付けることで次のような動作を確認できるはずです。

- https://script.google.com/(中略)/exec の結果
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/8326d93f-8354-c778-12e0-8ed6fcad2087.png)

- https://script.google.com/(中略)/exec/greeting/morning の結果
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/8863ddc4-2548-28d3-a88e-ad5bd3a9f42b.png)

- https://script.google.com/(中略)/exec/greeting/afternoon の結果
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/0d04849c-1df1-8296-f4f0-ab4d78ce76f0.png)

- https://script.google.com/(中略)/exec/greeting/evening の結果
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/b60b84da-6830-8f40-5f4b-515f9f596f0f.png)

### パラメータの処理（GET編）

GASでGETリクエストのクエリパラメータを受け取るには doGet関数 の引数として与えられるイベントオブジェクトのparameterプロパティを参照します。

以下はGETリクエストのクエリパラメータを文字列に埋め込んで表示するAPIのサンプルです。

```javascript
function doGet(e) {
  let name = e.parameter.name;
  let age = e.parameter.age;
  let response =  ContentService.createTextOutput(
    `パラメータ： name = ${name}, age = ${age}`
  );
  response.setMimeType(ContentService.MimeType.TEXT);
  return response;
}
```

上記のAPIがデプロイできたら、デプロイURLの末尾にクエリパラメータを付けることで次のような動作を確認できるはずです。

- https://script.google.com/(中略)/exec?name=__SATO__&age=33 の結果
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/9ba68314-cf27-157e-1d25-be185b3d90b0.png)

### パラメータの処理（POST編）

GASでPOSTリクエストのリクエストボディパラメータを受け取るには doPost関数 の引数として与えられるイベントオブジェクトのpostDataプロパティを参照します。

以下はPOSTリクエストのリクエストボディパラメータを文字列に埋め込んで表示するAPIのサンプルです。

```javascript
function doPost(e) {
  let postData = JSON.parse(e.postData.contents);
  let name = postData.name;
  let age = postData.age;
  let response = ContentService.createTextOutput(
    JSON.stringify({"user input": `name=${name}, age=${age}`})
  );
  response.setMimeType(ContentService.MimeType.JSON);
  return response;
}
```

doPost関数が保存出来たら以下の設定でAPIをデプロイしてください。

- デプロイの種類：ウェブアプリ
- 次のユーザーとして実行：自分
- アクセスできるユーザー：全員

![スクリーンショット 2024-11-22 234902.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/a856dc18-5b0e-df19-af5e-e6e7824b3ce4.png)

デプロイが完了したら、次のようなcurlコマンドでAPIの動作を確認しましょう。

```sh
curl -L -H "Content-Type: application/json" -d '{"name":"SATO","age":"33"}' https://script.google.com/(中略)/exec
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939665/e9e0420a-bd25-fc76-d18e-9a2a36e3aac0.png)

:::note warn
doPostの利用について
<details>
doPost関数には認証の仕組みがないため、URLエントリポイントさえ知っていれば誰でも実行できてき舞います。
これは安全な状態ではないので、APIの設計・運用については十分に注意してください。
</details>
:::