Firebase Functions + TypeScript で CORS を使用する

Web API のプロトタイプを Firebase Functions の http ハンドラーを使って実装して、それを JavaScript から呼び出したら、  **No 'Access-Control-Allow-Origin' header** のエラーが出たので、それに対応した手順を書きます。

## まず

Firebase Functions を REST API のように **即時に応答を要求する用途** に使うのはほとんどの場合間違っています。一般的に、クラウドプラットフォームで提供される "Functions" と呼ばれる機能は、起動は遅いと考えたほうがよいです。

## Functions 

次のような ``say`` 関数を作りました、TypeScript で。

```typescript
import * as functions from 'firebase-functions';
export const say = functions.https.onRequest((request, response) => {
    response.send("Hello from Firebase!");
});
```

サンプルまんまです。

``firebase serve`` をして、ローカルで動作させると、
``http://localhost:5001/＜project名＞/us-central1/say`` のような URL で起動できます。

## HTML

これを呼び出す HTML を次のように書きました。
この ``index.html`` は Firebase Hosting に配置するので、 ``public`` ディレクトリに置きます。

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Firebase Hosting</title>
    <script type="text/javascript">
    function loadHello() {
      var label = document.getElementById("label");

      var req = new XMLHttpRequest();
      req.onreadystatechange = function() {
        if (req.readyState == 4) { 
          if (req.status == 200) {
            label.innerHTML = req.responseText;
          }
        } else {
          label.innerHTML = "通信中...";
        }
      }
      
      req.open('GET', 'http://localhost:5001/＜プロジェクト名＞/us-central1/say', true);
      req.send(null);
    }
    </script>
  </head>
  <body onLoad="loadHello()">
    <H1>Firebase Hosting</H1>
    <div id="label">loading..</div>
  </body>
</html>
```

こちらも ``firebase serve`` しているときに ``http://localhost:5000/`` でアクセスできます。

## *No Access-Control-Allow-Origin エラー

``index.html`` は、読み込み時に Functions の ``/say`` を呼び出して、そのレスポンスを ``id=label`` に表示する、というものですが、読み込み時に Console にエラーが出ます。

```
Failed to load http://localhost:5001/xxxx/us-central1/say: 
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://localhost:5000' is therefore not allowed access.
```

よくあるやつです。

## Functions で CORS を使う

Functions を CORS に対応させる（別ドメインからの呼び出しを許可する）には、[expressjs/cors: Node.js CORS middleware](https://github.com/expressjs/cors) という node.js 用ライブラリを使います。今回は TypeScript なので、これの type definitions である

* [@types/cors - npm](https://www.npmjs.com/package/@types/cors)

を使います。

まずは cors と @types/cors をインストールします。

```
npm install --save cors
npm install --save-dev @types/cors
```

``functions/package.json`` は次のようになっています。

```json
{
  "name": "functions",
  "scripts": {
    "lint": "tslint --project tsconfig.json",
    "build": "tsc",
    "serve": "npm run build && firebase serve --only functions",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "main": "lib/index.js",
  "dependencies": {
    "cors": "^2.8.4",
    "firebase-admin": "~5.12.1",
    "firebase-functions": "^1.0.3"
  },
  "devDependencies": {
    "@types/cors": "^2.8.4",
    "tslint": "^5.8.0",
    "typescript": "^2.5.3"
  },
  "private": true
}
```

続いて Functions を次のように書き換えます。

```typescript
import * as functions from 'firebase-functions';
import * as corsLib from 'cors';
const cors = corsLib();

export const say = functions.https.onRequest((request, response) => {
    return cors(request, response, () => {
        response.send("Hello from Firebase!");
    })
});
```

cors を import して関数として実行。
さらに ``onRequest`` の中を、 ``cors(req, res, ()=>{  })`` で包んじゃいます。

これで html を表示させると、エラーは消え、次のような画面が表示できます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/8227/ddb27970-0bec-1481-aaa8-15d10c162f6f.png)