GoogleAPIを実行してデータを取得するAPIをAWSのLambdaとAPI Gatewayでつくる

# 記事の概要

非公開のGoogleスプレッドシートの情報を取得する関数をLambdaで作成  
API Gatewayで呼び出すところまでの手順を記録

- 利用サービス
  - Google Cloud APIs
  - AWS Lambda
  - AWS API Gateway

# 前提

- javascriptで何かしらの開発した経験がある
- Google Cloud Platformアカウントを所持している
- AWSアカウントを所持している

# 作業手順目次

1. Google Cloud APIs
    1. プロジェクトの作成
    1. Google Sheets API ライブラリの有効化
    1. 認証情報の追加
    1. 鍵の作成
1. Lambda
    1. 関数の作成
    1. node_modulesのLayers登録
    1. 環境変数の登録（Googleの認証情報）
1. API Gateway
    1. APIの作成
    1. リソースの作成
    1. メソッドの作成
    1. APIのデプロイ
    1. 制限の設定
1. APIの呼び出し
    1. サンプルコード


## Google Cloud APIs

:::note info
Google Cloud Platform
Googleが提供しているクラウドコンピューティングサービス  
Google Cloud APIsの利用にはこのアカウントが必要
:::

:::note info
Google Cloud APIs
Googleが提供するサービスのデータを操作することができるAPIを  
インターネットを通じて実行できるサービス
:::

### プロジェクトの作成

- Google Cloud Platformで新しいプロジェクトを作成
  - https://console.cloud.google.com/home/dashboard
  - プロジェクト名は自由

![report_2022-01-03-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/64e11c71-1d17-7a18-bcf5-78a826c25fc7.png)
![report_2022-01-03-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/a1be6667-a0d6-f614-39a8-1ff96f87170e.png)


### ライブラリの有効化

プロジェクトで利用するAPIを有効化する

1. APIとサービス > ライブラリ
1. `Google Sheets API`を選択
1. 有効にする

![report_2022-01-03-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/89fa4732-516e-cf02-0d50-5caa371220b8.png)
![report_2022-01-03-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/d4550945-acb3-9880-502f-38a3695ab01f.png)
![report_2022-01-03-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/347c55b7-b626-a8d7-2818-70d47e0b9360.png)


### 認証情報の追加

１. APIとサービス > 認証情報
1. 認証情報を作成 > サービスアカウント
1. アカウント名の入力 > 完了

![report_2022-01-03-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/d2fc779a-f3fe-b603-2a05-d736f76c2c34.png)
![report_2022-01-03-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/ab77b81c-f28a-513d-88e2-749ce0cbc918.png)
![report_2022-01-03-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/96661c13-c42a-5cbf-8e00-07194df5fd88.png)
![report_2022-01-03-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/19d7c760-cb71-cbec-383b-b2bbb9fae19f.png)
![report_2022-01-03-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/fcb36ec3-713e-656a-e409-e8cf42b0b22c.png)


### 鍵の作成

Lambdaで利用するための認証鍵の作成

1. サービスアカウントの詳細 > キー > 鍵の作成
1. キータイプ JSON > 作成
1. jsonがダウンロードできる（あとで内容をLambdaに登録する）

![report_2022-01-03-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/e2e834d7-b5ea-1344-84f6-48373809c4a9.png)
![report_2022-01-03-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/f780147d-5c3a-cd95-efe1-81beb5667239.png)
![report_2022-01-03-16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/e8e5786c-2c34-887b-46aa-32d1c5254cc0.png)
![report_2022-01-03-17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/091467e6-9f05-8a97-f58e-a81ac4fdb07a.png)


## AWS　Lambda

:::note info
AWS(Amazon Web Services)
Amazon.comにより提供されているクラウドコンピューティングサービス
:::

:::note info
Lambda
サーバーレスで開発したプログラムを実行できるようにするサービス
:::

### 関数の作成

- [関数名] sample-test（任意の名前でOK）
- [作成方法] 一から作成
- [ランタイム] Node.js 14.x
- [アーキテクチャ] x86_64

![report_2022-01-03-20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/c193fd5c-49fa-57ec-65dc-1a3ab48ccd98.png)
![report_2022-01-03-21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/fd31a421-78f0-9ce8-a2bd-b660ffefdb21.png)
![report_2022-01-03-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/e3871464-5f70-948d-6967-d33ba0f53c9b.png)



**新規作成直後のコード**

```javascript
exports.handler = async (event) => {
    // TODO implement
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

```
### node_modulesのLayers登録

:::note info
AWS Lambda Layers
複数のLambda関数で外部ライブラリなどを共有できる仕組み
:::

Lambdaでは、ファイル量が多いとブラウザ操作が制限されるため  
node_modulesを含めたコードソースをZIPで登録した場合  
ブラウザ上で関数の編集や閲覧ができなくなる  

この状態は関数作成時に不便なため  
使っているnode_modulesを`AWS Lambda Layers`に登録することで回避する  

登録したレイヤーは関数の「レイヤー」に追加して利用できる  
また、他の関数作成時にも同じレイヤーを利用することができる

1. レイヤーの一覧 > 作成
1. node_modules（以下package.jsonをローカルで展開し）をzipにしてアップロード
1. レイヤーの追加

package.json 
```
{
  "dependencies": {
    "googleapis": "^92.0.0"
  }
}
```

![report_2022-01-03-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/c53b149a-6406-12f4-9e65-66fff22db2bf.png)
![report_2022-01-03-25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/37161749-2a24-865c-d4c9-db00eb43b7e8.png)
![report_2022-01-03-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/67f51ae0-3825-5dc8-43cf-cfdfab682156.png)
![report_2022-01-03-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/05a7acf0-000d-74fa-aced-eab5b9a0829b.png)
![report_2022-01-03-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/fa03532b-309a-d6c3-a592-b3b5c345893b.png)


### 環境変数の登録

1. 関数の詳細 > 設定 > 環境変数
1. 環境変数の編集 で 「環境変数の追加」を押す
1. aキー名 と 値 を入力 し保存

![report_2022-01-03-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/756ae6f9-8759-9286-e32c-87e11c1f67ea.png)
![report_2022-01-03-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/003c67b8-3121-67db-a531-ee35b3297fe9.png)
![report_2022-01-03-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/da790d74-9d3d-707f-d667-cb046756ada0.png)

### GoogleAPIからデータを取得して返すコードの作成

```
// レイヤー_登録したnode_modulesにgoogleapisを含めている前提
const { google } = require('googleapis');
const sheets = google.sheets('v4');

// 登録した環境変数の取得
const SERVICE_ACCOUNT_KEY = JSON.parse(process.env.SERVICE_ACCOUNT_KEY);

// 外部ドメインからAPIを叩くのでクロスドメインの許可
const HEADERS = {
  "Access-Control-Allow-Headers" : "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token",
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "OPTIONS,POST,GET"
};

async function execAPI(spreadsheetId, range) {
  // 利用するAPIは Google spreadsheetsの情報を取得するspreadsheets
  const auth = await google.auth.getClient({
    credentials: SERVICE_ACCOUNT_KEY,
    scopes: ['https://www.googleapis.com/auth/spreadsheets'],
  });

  const apiOptions = {
    auth,
    spreadsheetId,
    range,
  };

  return sheets.spreadsheets.values.get(apiOptions);
}

exports.handler = async (event) => {
  // event?.pathParameters でAWS API Gatewayで作成したパスパラメータを取得
  const spreadsheetId = event?.pathParameters?.spreadsheetId;
  const range = event?.pathParameters?.range ? decodeURI(event?.pathParameters?.range) : 'シート1';

  let response = {};
  let responseBody = {}
  await execAPI(spreadsheetId, range).then((t) => {
    responseBody = t;
  })
  // getメソッドとして返す場合のオブジェクト形式
  response = {
    "statusCode": 200,
    "body": JSON.stringify(responseBody),
    "headers": HEADERS,
  };
  return response;
};

```

## AWS API Gateway

Lambdaで作成した関数を呼び出せるREST APIの作成を行う

:::note info
AWS API Gateway
簡単にAPIの作成、配布、保守、監視、保護が行えるマネージドサービス
:::

1. APIの作成
1. リソースの作成
    1. API Gateway CORSを有効にする
1. メソッドの作成
1. APIのデプロイ
1. 制限の設定

### APIの作成

1. APIの作成
2. REST APIを構築
2. API > 作成
    - [プロトコル] REST 
    - [新しいAPIの作成] 新しいAPI
    - [API名] 半角英数字
    - [エンドポイント] リージョン


![report_2022-01-03-34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/1f4b7ef4-374a-433d-9d90-83d1570104e2.png)
![report_2022-01-03-35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/1314197c-5a4c-f479-138e-9e852a787cda.png)
![report_2022-01-03-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/f6560f54-cf40-7e6f-83ce-8e2d6011e365.png)


### リソースの作成

- リソースの作成
  - /test
    - /{spreadsheetId}
      - /{range}
       - API Gateway CORSを有効にする

![report_2022-01-03-37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/8ea69cc0-dbe0-a0ed-a79a-0edd69f745fd.png)
![report_2022-01-03-38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/eff6aec0-090d-2898-4320-f4edcab50367.png)
![report_2022-01-03-39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/ef477ad5-b148-fd18-beeb-a39b1d4f564b.png)


### メソッドの作成

- メソッドの作成 > GET
- Lambda関数を実行します

![report_2022-01-03-40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/2ccfabd1-1258-8e78-dc1e-0b2a8a6907de.png)
![report_2022-01-03-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/183dd444-13c7-6472-fb0a-b23a50941b55.png)
![report_2022-01-03-42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/431659e7-db3c-c1b6-97e6-eb02a7aa5837.png)
![report_2022-01-03-43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/c56736a0-1c87-9201-c7fe-5d939b97a470.png)

### APIのデプロイ

作ったAPIを公開する

- プロシキの統合
    - クロスドメインで実行できるようにするため
- アクション > APIのデプロイ
- レートの変更
    - 必要に応じて安全なレートに変更する
    - レート 10、バースト 5　は1ヶ月呼び出され続けても一万円ほどでおさまる想定の値

![report_2022-01-03-45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/b471eaeb-d062-6d0b-3800-d04d0086b02c.png)
![report_2022-01-03-46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/0d8aca34-5156-5036-82c0-9c2ad7fab38c.png)
![report_2022-01-03-47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/cf01077a-b779-7527-0a53-0082da10b11b.png)


### 制限の設定

セキュリティを考慮して呼び出し処理に制限をかける

- APIキーがなければ呼び出せない
- APIキーを使っていても設定したプラン以上の呼び出しは行えない

手順

- APIキーの追加
- 使用量プランの作成
    - APIステージの追加
    - APIキーを使用プランに追加
- APIキーに使用料プランの設定

![report_2022-01-03-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/1a85f608-268c-6472-eddc-7791d57cd802.png)
![report_2022-01-03-50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/3b4bb0b6-2364-bfe1-d463-f77f2bcedc9e.png)
![report_2022-01-03-51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/cddd53bb-5ee5-bea4-08ad-1d8b63785e11.png)
![report_2022-01-03-53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/48eaa441-8fa4-39ee-8147-bb6787c21b9b.png)
![report_2022-01-03-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/8884da71-c8b0-6a7d-d239-34df141db670.png)


# APIの呼び出し

## サンプルコード

```html
<html>
<header>
  <script>
    function getLocalApi() {
      const sheetId = document.getElementById('sheetId').value;
      const range = document.getElementById('range').value || 'シート1'; 
  
      const request = new XMLHttpRequest()
      request.open('GET', `https://[ドメイン]/[ステージ名]/test/${sheetId}/${range}`)
      request.setRequestHeader('Content-Type', 'application/json');
      request.setRequestHeader('x-api-key', '[APIキー]');
      request.responseType = 'json'
      request.send();

      request.onerror = function(e){
        console.log(e);
        alert('データが取得できませんでした');
      };

      request.onload = () => {
        if (request.status === 200) {
          console.log(request.response);
          alert('データが取得できました');
        }
      };
    }
  </script>
</header>
<body>
  <input type="text" id="sheetId" name="sheetId" size="20" value=""  placeholder="sheetId" required>
  <input type="text" id="range" name="range" size="20" value="" placeholder="range" required>

  <hr  style="margin-top: 20px;">

  <button type="button" onclick="getLocalApi()">
    サービスアカウントからデータ取得
  </button>
</body>
</html>
```

コードに必要な ドメイン / ステージ名 / APIキー は以下の画面から取得

![report_2022-01-03-56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/b84025b3-64cc-6a93-3ad0-110f94ffebd7.png)

## 実行テスト

1. スプレッドシートを作成
2. Googleサービスアカウントユーザーを編集者に追加
3. スプレッドシートのシートIDで実行して取得完了！

![report_2022-01-03-57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/994091eb-b224-b848-4306-b7e08a7d166c.png)
![report_2022-01-03-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645692/75b9dafe-72f0-98ba-a195-afae9f4a215e.png)

# まとめ

外部サービスからGoogleのデータを取得できないか模索したところ
AWSについて教えてもらい、この機会に資料にまとめた。
クロスブラウザが可能な外部サービスであれば色んな面白いことができそう。

次回はこれを利用して作成したFigmaプラグインについてまとめる。

# 参考

### GoogleAPI

GoogleAPIをフロントで呼び出す時のサンプルコード

https://cloud.google.com/compute/docs/tutorials/javascript-guide

GoogleSheetsAPIのGETエンドポイントサンプルコード

https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/get

使ってないけどリダイレクトでAuth認証するための方法

https://note.yuuniworks.com/study/gapi.html#%E8%AA%8D%E8%A8%BC%E3%81%8B%E3%82%99%E4%B8%8D%E8%A6%81%E3%81%AE%E3%83%8F%E3%82%9A%E3%82%BF%E3%83%BC%E3%83%B3

### APIGateway

Lambda プロキシ統合の使用って何？？

https://qiita.com/yuuwatanabe/items/a3bd65e709f20574b6db

APIGatewayでクロスオリジンのためのOption関数を設定する話

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/how-to-cors-console.html

APIGatewayでパスパラメータを設定する方法

https://www.iga-soft.com/archives/1494

APIGatewayのAPIキー設定する方法

https://nasrinjp1.hatenablog.com/entry/2019/10/08/212938

### Lambda

Lambda関数のレスポンスの形式を正しく渡さないとクロスオリジン解決できないよって話の参考

https://stackoverflow.com/questions/35190615/api-gateway-cors-no-access-control-allow-origin-header

Lambda関数のレスポンスの形式を正しく渡さないとAPIGatewayから怒られる話

https://aws.amazon.com/jp/premiumsupport/knowledge-center/malformed-502-api-gateway/

Lambdaで環境変数を使う方法

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-envvars.html

https://shikaku-mafia.com/aws-lambda-environment/

Lambdaでnode_modulesもzipにしてアップロードしないといけなかった話

https://xp-cloud.jp/blog/2019/01/12/4630/