はじめに: 最初の関数の記述、テスト、デプロイ


Cloud Functions を使用するにあたり、まずはこのチュートリアルを実践してください。このチュートリアルでは、必要な設定タスクから始め、次の 2 つの関連する関数の作成、テスト、デプロイを実践します。

  • 「add message」関数は、テキスト値を受け取る URL を公開し、そのテキスト値を Cloud Firestore に書き込みます。
  • 「make uppercase」関数は、Cloud Firestore の書き込みでトリガーされ、テキストを大文字に変換します。

関数を含むサンプルコード全体を次に示します。

Node.js

// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.
const {logger} = require("firebase-functions");
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

// The Firebase Admin SDK to access Firestore.
const {initializeApp} = require("firebase-admin/app");
const {getFirestore} = require("firebase-admin/firestore");

initializeApp();

// Take the text parameter passed to this HTTP endpoint and insert it into
// Firestore under the path /messages/:documentId/original
exports.addmessage = onRequest(async (req, res) => {
  // Grab the text parameter.
  const original = req.query.text;
  // Push the new message into Firestore using the Firebase Admin SDK.
  const writeResult = await getFirestore()
      .collection("messages")
      .add({original: original});
  // Send back a message that we've successfully written the message
  res.json({result: `Message with ID: ${writeResult.id} added.`});
});

// Listens for new messages added to /messages/:documentId/original
// and saves an uppercased version of the message
// to /messages/:documentId/uppercase
exports.makeuppercase = onDocumentCreated("/messages/{documentId}", (event) => {
  // Grab the current value of what was written to Firestore.
  const original = event.data.data().original;

  // Access the parameter `{documentId}` with `event.params`
  logger.log("Uppercasing", event.params.documentId, original);

  const uppercase = original.toUpperCase();

  // You must return a Promise when performing
  // asynchronous tasks inside a function
  // such as writing to Firestore.
  // Setting an 'uppercase' field in Firestore document returns a Promise.
  return event.data.ref.set({uppercase}, {merge: true});
});

Python

# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import firestore_fn, https_fn

# The Firebase Admin SDK to access Cloud Firestore.
from firebase_admin import initialize_app, firestore
import google.cloud.firestore

app = initialize_app()


@https_fn.on_request()
def addmessage(req: https_fn.Request) -> https_fn.Response:
    """Take the text parameter passed to this HTTP endpoint and insert it into
    a new document in the messages collection."""
    # Grab the text parameter.
    original = req.args.get("text")
    if original is None:
        return https_fn.Response("No text parameter provided", status=400)

    firestore_client: google.cloud.firestore.Client = firestore.client()

    # Push the new message into Cloud Firestore using the Firebase Admin SDK.
    _, doc_ref = firestore_client.collection("messages").add({"original": original})

    # Send back a message that we've successfully written the message
    return https_fn.Response(f"Message with ID {doc_ref.id} added.")


@firestore_fn.on_document_created(document="messages/{pushId}")
def makeuppercase(event: firestore_fn.Event[firestore_fn.DocumentSnapshot | None]) -> None:
    """Listens for new documents to be added to /messages. If the document has
    an "original" field, creates an "uppercase" field containg the contents of
    "original" in upper case."""

    # Get the value of "original" if it exists.
    if event.data is None:
        return
    try:
        original = event.data.get("original")
    except KeyError:
        # No "original" field, so do nothing.
        return

    # Set the "uppercase" field.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    event.data.reference.update({"uppercase": upper})

このチュートリアルについて

このサンプルでは、Cloud Firestore と HTTP トリガー関数を選択しました。その理由の 1 つとしては、これらのバックグラウンド トリガーは Firebase Local Emulator Suite で十分にテストできるためです。このツールセットでは、Realtime DatabaseCloud Storage、Pub/Sub、Auth、HTTP 呼び出し可能トリガーもサポートされています。このページでは説明されていませんが、Remote Config、TestLab のトリガーなど、他の種類のバックグラウンド トリガーもツールセットを使用してインタラクティブにテストできます。

このチュートリアルの以下のセクションでは、サンプルをビルド、テスト、デプロイするために必要な手順について詳しく説明します。

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

  1. Firebase コンソールで [プロジェクトを追加] をクリックします。

    • Firebase リソースを既存の Google Cloud プロジェクトに追加するには、そのプロジェクト名を入力するか、プルダウン メニューから選択します。

    • 新しいプロジェクトを作成するには、任意のプロジェクト名を入力します。必要に応じて、プロジェクト名の下に表示されるプロジェクト ID を編集することもできます。

  2. Firebase の利用規約が表示されたら、内容を読み、同意します。

  3. [続行] をクリックします。

  4. (省略可)プロジェクトに対し Google Analyticsを設定します。これにより、次の Firebase プロダクトを使用する際のエクスペリエンスを最適化できます。

    既存の Google Analytics アカウントを選択するか、新しいアカウントを作成します。

    新しいアカウントを作成する場合は、Analytics レポートのロケーションを選択し、プロジェクトのデータ共有設定と Google Analyticsの規約に同意します。

  5. [プロジェクトを作成](既存の Google Cloud プロジェクトを使用する場合は [Firebase を追加])をクリックします。

Firebase プロジェクトのリソースが自動的にプロビジョニングされます。処理が完了すると、Firebase コンソールに Firebase プロジェクトの概要ページが表示されます。

環境と Firebase CLI を設定する

Node.js

関数を作成するには Node.js 環境が必要です。また、Cloud Functions ランタイムに関数をデプロイするには Firebase CLI が必要です。Node.js と npm をインストールする場合は、Node Version Manager をおすすめします。

Node.js と npm をインストールしたら、お好みの方法を使用して Firebase をインストールします。npm を使用して CLI をインストールするには、次のコマンドを使用します。

npm install -g firebase-tools

これにより、グローバルに使用できる firebase コマンドがインストールされます。コマンドが失敗した場合は、npm アクセス権の変更が必要になる場合があります。firebase-tools を最新バージョンに更新するには、同じコマンドを再実行します。

Python

関数を作成するには Python 環境が必要です。また、Cloud Functions ランタイムに関数をデプロイするには Firebase CLI が必要です。venv を使用して依存関係を分離することをおすすめします。Python バージョン 3.10 と 3.11 がサポートされています。

Python をインストールしたら、お好みの方法で Firebase CLI をインストールします。

プロジェクトの初期化

Firebase SDK for Cloud Functions を初期化するときに、依存関係と最小限のサンプルコードを含む空のプロジェクトを作成します。Node.js を使用している場合、関数を作成するために TypeScript または JavaScript を選択できます。このチュートリアルでは、Cloud Firestore も初期化する必要があります。

プロジェクトを初期化するには:

  1. firebase login を実行してブラウザからログインし、Firebase を認証します。
  2. Firebase プロジェクトのディレクトリに移動します。
  3. firebase init firestore を実行します。このチュートリアルでは、Firestore ルールとインデックス ファイルのプロンプトが表示されたら、デフォルト値をそのまま使用してかまいません。このプロジェクトでまだ Cloud Firestore を使用していない場合は、Cloud Firestore を使ってみるの説明に沿って、Firestore の開始モードとロケーションも選択する必要があります。
  4. firebase init functions を実行します。既存のコードベースを選択するか、新しいコードベースを初期化して名前を付けるように求められます。最初は、デフォルトの場所にある単一のコードベースで十分です。後で実装を拡張する場合は、コードベースの関数を整理することをおすすめします。
  5. CLI は次の言語をサポートしています。

    • JavaScript
    • TypeScript
    • Python

    このチュートリアルでは、JavaScript または Python を選択します。TypeScript での作成方法については、TypeScript での関数の作成をご覧ください。

  6. CLI には、依存関係をインストールするオプションがあります。依存関係を別の方法で管理する場合は、選択する必要はありません。

これらのコマンドが正常に完了すると、プロジェクト構造は次のようになります。

Node.js

myproject
+- .firebaserc    # Hidden file that helps you quickly switch between
|                 # projects with `firebase use`
|
+- firebase.json  # Describes properties for your project
|
+- functions/     # Directory containing all your functions code
      |
      +- .eslintrc.json  # Optional file containing rules for JavaScript linting.
      |
      +- package.json  # npm package file describing your Cloud Functions code
      |
      +- index.js      # Main source file for your Cloud Functions code
      |
      +- node_modules/ # Directory where your dependencies (declared in
                        # package.json) are installed

Node.js の場合、初期化時に作成された package.json ファイルには重要なキー "engines": {"node": "18"} が格納されています。これにより、関数の作成とデプロイ用の Node.js バージョンを指定します。他のサポート対象バージョンを選択することもできます。

Python

myproject
+- .firebaserc    # Hidden file that helps you quickly switch between
|                 # projects with `firebase use`
|
+- firebase.json  # Describes properties for your project
|
+- functions/     # Directory containing all your functions code
      |
      +- main.py      # Main source file for your Cloud Functions code
      |
      +- requirements.txt  #  List of the project's modules and packages 
      |
      +- venv/ # Directory where your dependencies are installed

必要なモジュールをインポートしてアプリを初期化する

設定が完了したら、以降のセクションで説明するように、ソース ディレクトリを開いてコードを追加します。このサンプルでは、プロジェクトは Cloud Functions と Admin SDK モジュールをインポートする必要があります。ソースファイルに次のような行を追加します。

Node.js

// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.
const {logger} = require("firebase-functions");
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

// The Firebase Admin SDK to access Firestore.
const {initializeApp} = require("firebase-admin/app");
const {getFirestore} = require("firebase-admin/firestore");

initializeApp();

Python

# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import firestore_fn, https_fn

# The Firebase Admin SDK to access Cloud Firestore.
from firebase_admin import initialize_app, firestore
import google.cloud.firestore

app = initialize_app()

これらの行によって必要なモジュールが読み込まれ、Cloud Firestore の変更が可能な admin アプリ インスタンスが初期化されます。Admin SDK サポートは FCMAuthenticationFirebase Realtime Database に対応しているため、これが利用可能な場所ではどこでも、Cloud Functions を使用して効果的に Firebase を統合できます。

Firebase CLI では、プロジェクトの初期化時に Firebase Admin SDK と Firebase SDK for Cloud Functions モジュールが自動的にインストールされます。サードパーティ ライブラリをプロジェクトに追加する方法については、依存関係を処理するをご覧ください。

メッセージを追加する関数の追加

メッセージを追加する関数では、次の行をソースファイルに追加します。

Node.js

// Take the text parameter passed to this HTTP endpoint and insert it into
// Firestore under the path /messages/:documentId/original
exports.addmessage = onRequest(async (req, res) => {
  // Grab the text parameter.
  const original = req.query.text;
  // Push the new message into Firestore using the Firebase Admin SDK.
  const writeResult = await getFirestore()
      .collection("messages")
      .add({original: original});
  // Send back a message that we've successfully written the message
  res.json({result: `Message with ID: ${writeResult.id} added.`});
});

Python

@https_fn.on_request()
def addmessage(req: https_fn.Request) -> https_fn.Response:
    """Take the text parameter passed to this HTTP endpoint and insert it into
    a new document in the messages collection."""
    # Grab the text parameter.
    original = req.args.get("text")
    if original is None:
        return https_fn.Response("No text parameter provided", status=400)

    firestore_client: google.cloud.firestore.Client = firestore.client()

    # Push the new message into Cloud Firestore using the Firebase Admin SDK.
    _, doc_ref = firestore_client.collection("messages").add({"original": original})

    # Send back a message that we've successfully written the message
    return https_fn.Response(f"Message with ID {doc_ref.id} added.")

メッセージを追加する関数は HTTP エンドポイントです。エンドポイントに対するリクエストを行うと、リクエスト オブジェクトとレスポンス オブジェクトがプラットフォームのリクエスト ハンドラ(onRequest() または on_request)に渡されます。

呼び出し可能な関数と同様に HTTP 関数は同期的です。このため、できるだけ早くレスポンスを送信し、Cloud Firestore による作業を遅らせる必要があります。メッセージを追加する HTTP 関数は、テキスト値を HTTP エンドポイントに渡し、パス /messages/:documentId/original でデータベースに挿入します。

大文字に変換する関数の追加

大文字に変換する関数の場合は、ソースファイルに次の行を追加します。

Node.js

// Listens for new messages added to /messages/:documentId/original
// and saves an uppercased version of the message
// to /messages/:documentId/uppercase
exports.makeuppercase = onDocumentCreated("/messages/{documentId}", (event) => {
  // Grab the current value of what was written to Firestore.
  const original = event.data.data().original;

  // Access the parameter `{documentId}` with `event.params`
  logger.log("Uppercasing", event.params.documentId, original);

  const uppercase = original.toUpperCase();

  // You must return a Promise when performing
  // asynchronous tasks inside a function
  // such as writing to Firestore.
  // Setting an 'uppercase' field in Firestore document returns a Promise.
  return event.data.ref.set({uppercase}, {merge: true});
});

Python

@firestore_fn.on_document_created(document="messages/{pushId}")
def makeuppercase(event: firestore_fn.Event[firestore_fn.DocumentSnapshot | None]) -> None:
    """Listens for new documents to be added to /messages. If the document has
    an "original" field, creates an "uppercase" field containg the contents of
    "original" in upper case."""

    # Get the value of "original" if it exists.
    if event.data is None:
        return
    try:
        original = event.data.get("original")
    except KeyError:
        # No "original" field, so do nothing.
        return

    # Set the "uppercase" field.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    event.data.reference.update({"uppercase": upper})

「make uppercase」関数は、Cloud Firestore に書き込まれるときに実行され、リッスンするドキュメントを定義します。パフォーマンス上の理由から、可能な限り具体的にする必要があります。

中かっこ({documentId} など)はパラメータを囲みます。これは、コールバックで一致したデータを公開するワイルドカードです。新しいメッセージが追加されるたびに、Cloud Firestore はコールバックをトリガーします。

Node.js では、Cloud Firestore イベントなどのイベント駆動型の関数は非同期です。コールバック関数は、null、オブジェクト、Promise のいずれかを返す必要があります。何も返さない場合、関数はタイムアウトして、エラーを通知し、再試行されます。同期、非同期、Promise をご覧ください。

関数の実行をエミュレートする

Firebase Local Emulator Suite を使用すると、Firebase プロジェクトにデプロイする代わりにローカルマシンでアプリをビルドしてテストできます。開発中にローカルテストを行うことを強くおすすめします。その理由の 1 つとしては、本番環境でコストを発生させる可能性があるコーディング エラーのリスク(無限ループなど)が低下するためです。

関数をエミュレートするには:

  1. firebase emulators:start を実行し、Emulator Suite UI の URL の出力を確認します。デフォルトは localhost:4000 ですが、お使いのマシンによっては別のポートでホストされている場合があります。その URL をブラウザに入力して、Emulator Suite UI を開きます。

  2. firebase emulators:start コマンドの出力で、HTTP 関数の URL を確認します。この URL は http://localhost:5001/MY_PROJECT/us-central1/addMessage のような形式です。ただし、次の点に注意してください。

    1. MY_PROJECT はプロジェクト ID に置き換えられます。
    2. お使いのローカルマシンによってはポートが異なる場合があります。
  3. 関数の URL の末尾にクエリ文字列 ?text=uppercaseme を追加します。http://localhost:5001/MY_PROJECT/us-central1/addMessage?text=uppercaseme のようになります。メッセージ「uppercaseme」をカスタム メッセージに変更することもできます。

  4. ブラウザの新しいタブで URL を開いて、新しいメッセージを作成します。

  5. Emulator Suite UI で関数の動作を確認します。

    1. [ログ] タブに、HTTP 関数が正常に実行されたことを示す新しいログが表示されます。

      i functions: Beginning execution of "addMessage"

      i functions: Beginning execution of "makeUppercase"

    2. [Firestore] タブに、元のメッセージと大文字に変換されたメッセージを含むドキュメントが表示されます(元のメッセージが「uppercaseme」だった場合、変換後は「UPPERCASEME」となります)。

本番環境に関数をデプロイする

エミュレータで関数が意図したとおりに動作するようになったら、本番環境への関数のデプロイ、テスト、実行に進むことができます。本番環境にデプロイするには、プロジェクトが Blaze 料金プランを利用している必要がある点にご注意ください。Cloud Functions の料金をご覧ください。

チュートリアルを完了するには、関数をデプロイして実行します。

  1. 次のコマンドを実行して、関数をデプロイします。

     firebase deploy --only functions
     

    このコマンドを実行すると、Firebase CLI は HTTP 関数のエンドポイントの URL を出力します。デバイスには、次のような行が表示されます。

    Function URL (addMessage): https://us-central1-MY_PROJECT.cloudfunctions.net/addMessage
    

    この URL には、プロジェクト ID と HTTP 関数のリージョンが含まれています。ここでは必要ありませんが、本番環境の HTTP 関数では、ネットワーク レイテンシを最小限に抑えるため、ロケーションの指定が必要になる場合があります。

    「プロジェクトへのアクセスを許可できません」などのアクセスエラーが発生した場合は、プロジェクト エイリアスを確認してみてください。

  2. CLI によって出力された URL を使用して、テキスト クエリ パラメータを追加し、ブラウザで開きます。

    https://us-central1-MY_PROJECT.cloudfunctions.net/addMessage?text=uppercasemetoo
    

    関数によりブラウザが実行され、テキスト文字列が格納されているデータベースの場所にある Firebase コンソールにリダイレクトされます。この書き込みイベントによって、文字列を大文字に変換する関数がトリガーされます。

関数をデプロイして実行した後、Google Cloud コンソールでログを表示できます。開発環境または本番環境で関数を削除する必要がある場合は、Firebase CLI を使用します。

本番環境では、実行するインスタンスの最小数と最大数を設定して関数のパフォーマンスを最適化し、コストを抑えられます。これらのランタイム オプションの詳細については、スケーリング動作を制御するをご覧ください。

次のステップ

このドキュメントでは、Cloud Functions関数の管理方法と、Cloud Functions でサポートされているすべてのイベントタイプを処理する方法について説明します。

Cloud Functions の詳細については、以下もご覧ください。