0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Firebase Emulatorで動作確認しながらREST APIを自作してFirestoreにデータを保存させ、functionをデプロイする

Last updated at Posted at 2022-08-20

(2)CloudFunctions for Firebaseに触れる

概要

前回まででフロントエンドのReactアプリとfirebaseの設定とfunctionsの雛形の作成が完了しました。
前回:https://qiita.com/sugimo-ne/items/c363342a0d83c7ab82b4
今回はfunctionsをどのように作成して動作させるのか、簡単にRESTAPIの作成とCloud Firestoreとの連携もしてみたいと思います。

functionsに触れる。

こちらの公式ドキュメントに沿ってサンプルのコードを動かしていきます。

firebase functionsエンドポイント作成とレスポンスの設定をしてみる。

まずその前に、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 emulatorが起動されていない場合、

$ firebase emulators:start

で起動させ、
index.jsのコメントアウトを外してみてください。
コンソールにfunctionのローカルのエンドポイントが出力されるかとおもます。

*スクショ

 http://localhost:5001/プロジェクト名/us-central1/helloWorld

こちらのエンドポイントブラウザからアクセスしてみましょう。

スクリーンショット 2022-08-20 17.42.42.png

functions/index.jsの

exports.helloWorld = functions.https.onRequest((request, response) => {
  functions.logger.info("Hello logs!", {structuredData: true});
  response.send("Hello from Firebase!");
});

helloWorldが実行されてresponse.sendで指定した文字列がリクエストに対するレスポンスとして帰ってきています。

試しに

exports.helloWorld

こちらの記述を

exports.sample

と書き換え、

response.send("Hello from Firebase!");

response.send("yaa");

と書き換えてみます。

先ほどと変わって

 http://localhost:5001/プロジェクト名/us-central1/sample

こちらにアクセスするとyaaという文字列が返却されているのがわかるかと思います。

(request, response)を引数に取り、responseのメソッドを使用したものを関数を与え、
返り値を変数に格納し、exportすると変数名に対応したアクセスがあった際に実行されるためresponseの内容が返ってくるようになります。

公式のサンプルを動かす。

次にcloudfunctionsでfirebaseの提供するサービスと連携するscriptを見てみます。

公式のサンプルに従ってローカルで起動しているcloudfirestoreと連携したscriptを動かしてみます。

functions/index.jsの中身をサンプルに入れ替えます。

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

  exports.makeUppercase = functions.firestore.document('/messages/{documentId}')
      .onCreate((snap, context) => {
        const original = snap.data().original;
        console.log('Uppercasing', context.params.documentId, original);
        const uppercase = original.toUpperCase();
        return snap.ref.set({uppercase}, {merge: true});
      });
  

中身を見ていくと

functions.firestore.document()の引数にfirestoreのcollectionのドキュメントまでのパスを与え、.onCreate関数でドキュメントが作成された際の処理を記述しています。
ここではmessagesコレクションに作成されたドキュメントのoriginalフィールドの値を取得し、すべて英数字の大文字に変換し同じドキュメント内のussercaseフィールドに保存する処理が記述されています。

firebase emulatorにアクセスし、firestoreのタブを開きます。
スクリーンショット 2022-08-20 18.41.34.png

[start collection]ボタンを押して、

スクリーンショット 2022-08-20 19.38.28.png
collectionIDをmessages

DocumentIDをデフォルト値

Fieldをoriginal

Valueをtest
スクリーンショット 2022-08-20 19.38.41.png

としてsaveします。

そうするとローカルのEmulatorでデータベースの値が更新されているのが確認できます。
スクリーンショット 2022-08-20 19.39.14.png

functionsにexpressを使用してREST APIを作成し、データをローカルのfirestoreに保存する。

これまでやった内容をもとに
RESTAPIを作成してfirestoreのデータを追加してみましょう。

$ cd functions
$ npm install --save-dev firebase-admin express cors

必要なパッケージ類をインストールします。

ソースコード

functions/admin.js

const admin = require("firebase-admin");
admin.initializeApp();

const db = admin.firestore();

module.exports = {
  db,
};

admin.jsにfirebase-admin(firebaseをサーバサイドで利用するためのsdk)を使うための初期設定を行なっています。
ローカルのemulatorに対して操作を行う際にはinitializeApp()の引数は与えなくて大丈夫です。
controllerで使用するdbもインスタンス化してexportします。

functions/controllers/TestController.js

const {db} = require("../admin");

const messagePost = async (req, res) => {
    try{
        console.log(req.body)
        const {text} = req.body;
        const ref = db.collection("messages");
        const docRef = await ref.add({
            original: text
        })

        const docSnapshot = await docRef.get();
        const newMessage = {
            id: docSnapshot.id,
            ...docSnapshot.data(),
          };

        res.json({
            message: "success",
            data:newMessage
        })
    }catch(e){
        console.log(e)
        res.json({
            message: "failed",
            error:e
        })
    }
}

module.exports = {
    messagePost
}

messagePost関数はexpressのrouterでルーティングにマッチしたpostリクエストを受け取った際に発火させます。
リクエストボディのtextを受け取ってadmin sdkを使用してfirestoreにデータを保存する処理を記述しています。

これによってtextデータを含めたリクエストがきた際に、
messagesコレクションに新しく、
original: textの中身
のフィールドを持ったドキュメントが追加されるようになります。

functions/routes.js

const {Router} = require("express");
const router = new Router();

const {messagePost} = require("./controllers/TestController");

router.route("/test").post(messagePost);

module.exports = router;

routes.jsではexpressのルーティング機能を利用するための設定を行なっています。
controllerのmessagePost関数を読み込んで
/testに対してpostリクエストがあった際にmessagePostを呼び出します。

functions/index.js

const functions = require("firebase-functions");
const express = require("express");
const cors = require("cors");
const app = express();

app.use(cors({origin: true}));

const router = require("./routes");
app.use("/", router);

exports.api = functions.https.onRequest(app);

exports.makeUppercase = functions.firestore.document("/messages/{documentId}")
    .onCreate((snap, context) => {
      const original = snap.data().original;
      console.log("Uppercasing", context.params.documentId, original);
      const uppercase = original.toUpperCase();
      return snap.ref.set({uppercase}, {merge: true});
    });

index.jsで

  • expressの初期化
  • corsの設定
  • ミドルウェアの設定(routes.jsからrouterを読み込みmidlleware化する。)
  • apiモジュールのexport

を追加しました。
これによって/api/*にhttpリクエストがきた際に、
expressのrouterがルーティングにマッチした処理を行います。

ソースコードの編集は以上です。

動作確認

動作確認にはPOSTMANを利用して、
bodyにtextを含めて作成したAPIに対してリクエストを送信します。

すると正しく処理ができた場合にレスポンスとして作成されたデータのがjson形式でレスポンスとして返ってきていることがわかります。

Emulatorで確認しみましょう。

スクリーンショット 2022-08-20 22.54.52.png

作成したAPIを呼び出してデータが保存されていることと、
公式のサンプルコードの処理が走ってuppercaseフィールドに、大文字に変換された値が保存されているのがわかります。

デプロイしてみる。

実際にデプロイして動作確認を行います、見てデプロイの仕方を覚えるだけでも大丈夫ですが興味のある方は、従量課金の設定が必須となるので任意で進めてみてください。

スクリーンショット 2022-08-20 23.09.27.png

firebaseコンソールでsparkプランとなっている場合firebaseの関数はデプロイできないので従量課金プランに変更します。

その後/functionsより一つ上の階層で

$ firebase deploy --only functions

とコマンドを実行します。
するとcliでデプロイ処理が行われます。
lintの設定で引っかかるとデプロイできないので引っかかった方はコードを修正したりeslint.rcの内容を編集するなどしてください。

スクリーンショット 2022-08-20 23.29.03.png

こちらが出たら成功です。

firebaseのコンソールを開き、
スクリーンショット 2022-08-20 23.31.51.png
Functionsを開きます。
スクリーンショット 2022-08-20 23.32.36.png
二つの関数が作成されていて
api関数のトリガーの部分のリクエストにurlが貼ってあるのでそのurlをコピーします。

その後、postmanのエンドポイントをコピーしたurlに変えてpostしてみます。
スクリーンショット 2022-08-20 23.34.20.png

レスポンスが正常に返ってきたらfirebaseのコンソールを確認します。
スクリーンショット 2022-08-20 23.36.04.png

ローカルのEmulatorではなく本番のfirebaseにデータが保存されているのがわかります。

まとめ

ここまででfirebase上でAPIを自作し、firestoreにデータを保存させたり、
firestoreにデータが保存されるのをトリガーに処理を走らせることができるようになりました。

フロントエンドでアプリを作って連携させたり、応用すればそれなりに動くものが作れるようになるはずです。

次回からはいよいよ今回作るタスク管理アプリの概要から実装まで順を追って行なっていきたいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?