[メモ]Cloud Functions + Node.jsでさくっとAPI開発

たまには`firebase`でAPIを作りたくなったので、作ってみました。
次気が向いたとき、さくっと作れるように。

```
firebase --version 
9.1.0
```

## プロジェクトを作成する

まずGUIでプロジェクトを作成し、課金モードにしておくのが楽。

```
https://console.firebase.google.com/u/0/
```

```
cd desktop
mkdir api
cd api
```
## 連携/初期化
### 必要に応じて、有効化する。ひとまずFunctionsだけ有効化してみる。
```
$ mkdir api 
$ cd api
$ firebase init

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  /Users/user/Desktop/api

? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. 
 ◯ Database: Configure Firebase Realtime Database and deploy rules
 ◯ Firestore: Deploy rules and create indexes for Firestore
❯◉ Functions: Configure and deploy Cloud Functions
 ◯ Hosting: Configure and deploy Firebase Hosting sites
 ◯ Storage: Deploy Cloud Storage security rules
 ◯ Emulators: Set up local emulators for Firebase features
 ◯ Remote Config: Get, deploy, and rollback configurations for Remote Config
```

```
=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add, 
but for now we'll just set up a default project.

? Please select an option: (Use arrow keys)
❯ Use an existing project 
  Create a new project 
  Add Firebase to an existing Google Cloud Platform project 
  Don't set up a default project 
```

```

? Please select an option: Use an existing project
? Select a default Firebase project for this directory: 
  xxxxxxxx (yyyy) 
❯ api-189bf (api) 
  xxxxxxxx (yyyyy) 
  xxxxxxxx (yyyyy) 
```

```
=== Functions Setup

A functions directory will be created in your project with a Node.js
package pre-configured. Functions can be deployed with firebase deploy.

? What language would you like to use to write Cloud Functions? (Use arrow keys)
❯ JavaScript 
  TypeScript 
```
```
? What language would you like to use to write Cloud Functions? JavaScript
? Do you want to use ESLint to catch probable bugs and enforce style? Yes
✔  Wrote functions/package.json
✔  Wrote functions/.eslintrc.json
✔  Wrote functions/index.js
✔  Wrote functions/.gitignore
? Do you want to install dependencies with npm now? Yes

> protobufjs@6.10.2 postinstall /Users/inoueyousuke/Desktop/api/functions/node_modules/protobufjs
> node scripts/postinstall

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...
i  Writing gitignore file to .gitignore...

✔  Firebase initialization complete!
```

## helloWorldをデプロイ

```./functions/index.js

const functions = require('firebase-functions');

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
//   functions.logger.info("Hello logs!", {structuredData: true});
//   response.send("Hello from Firebase!");
// });
```
###コメントアウトを削除する

```./functions/index.js

const functions = require('firebase-functions');

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
 exports.helloWorld = functions.https.onRequest((request, response) => {
   functions.logger.info("Hello logs!", {structuredData: true});
   response.send("Hello from Firebase!");
 });
```

###デプロイ！！

```
firebase deploy

=== Deploying to 'api-189bf'...

i  deploying functions
Running command: npm --prefix "$RESOURCE_DIR" run lint

> functions@ lint /Users/user/Desktop/api/functions
> eslint .

✔  functions: Finished running predeploy script.
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
⚠  functions: missing required API cloudbuild.googleapis.com. Enabling now...
✔  functions: required API cloudfunctions.googleapis.com is enabled
✔  functions: required API cloudbuild.googleapis.com is enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (33.28 KB) for uploading
✔  functions: functions folder uploaded successfully
i  functions: creating Node.js 12 function helloWorld(us-central1)...
✔  functions[helloWorld(us-central1)]: Successful create operation. 
Function URL (helloWorld): https://us-central1-api-189bf.cloudfunctions.net/helloWorld

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/api-189bf/overview
```
## 生成されたエンドポイントにアクセスする

|![スクリーンショット 2021-03-26 3.15.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488939/87ca64f4-558b-be9c-e349-e008219e627b.png)|
|:-:|

### 完成


## フロントから叩けるPOSTAPIを作る
### CORSが面倒なので、以下参照で回避

```.functions/index.js
const functions = require("firebase-functions");

exports.post = functions.https.onRequest(async (request, response) => {

  //CORS用にAccess-Control-Allow系ヘッダを追加
  response.set("Access-Control-Allow-Origin", "https://xxxxxx.com");
  //DELETEだけは拒否
  response.set("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS, POST");
  //Content-Typeのみを許可
  response.set("Access-Control-Allow-Headers", "Content-Type");
  // dataフィールドに渡したい値は入れる
  response.json({ data: "hello!!" });
});
```

## Cloud Funtionsでnpmモジュールを使う

#### ./functions
```
npm install xxx
```
あとは普通にindex.jsを書くだけ。

## Fire Storeとか使いたい
上の章を参考に導入。
[こちらの記事参照](https://qiita.com/kaba/items/35a82b470354b9db29ba)

```
npm i firebase-admin --save
```

```./functions/index.js
const functions = require('firebase-functions')
// cloud functionでfirestoreを使うのに必要な設定は以下の２行
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)

// データベースの参照を作成
var fireStore = admin.firestore()

exports.helloWorld = functions.https.onRequest((request, response) => {
  // 動作確認のため適当なデータをデータベースに保存
  var citiesRef = fireStore.collection('cities');
  citiesRef.doc('SF').set({
    name: 'San Francisco', state: 'CA', country: 'USA',
    capital: false, population: 860000 })

  var cityRef = fireStore.collection('cities').doc('SF')
  cityRef.get()
  .then(doc => {
    if (!doc.exists) {
      response.send('No such document!')
    } else {
      response.send(doc.data())
      }
    })
    .catch(err => {
      response.send('not found')
    })
})
```

ひとまずこんな感じで幸せになれます！
エミュレータとかも書きたかったけど、また気が向いたときにやってみよう。

バイバイ！


## 参照
[cloud functionsでfirestoreを使う](https://qiita.com/kaba/items/35a82b470354b9db29ba)
[Markdown の画像に枠線をつける](https://qiita.com/nori-dev-akg/items/b9f1709418f39a473d08)
[FirebaseのCloud FunctionsでCORSが~とかAccess-Control-Allow-Originが~と言われたらこれ](https://qiita.com/qrusadorz/items/40234ac0b5c5c2315cad)