Cloud Functions for Firebase のクライアント SDK を使用すると、Firebase アプリから関数を直接呼び出すことができます。この方法でアプリから関数を呼び出すには、Cloud Functions で HTTP 呼び出し可能関数を記述してデプロイし、アプリから関数を呼び出すためのクライアント ロジックを追加します。
HTTP 呼び出し可能関数は HTTP 関数と類似しているものの、同一ではないことに注意することが重要です。HTTP 呼び出し可能関数を使用するには、プラットフォームのクライアント SDK をバックエンド API とともに使用する(またはプロトコルを実装する)必要があります。呼び出し可能関数と HTTP 関数の主な違いは以下のとおりです。
- 呼び出し可能関数では、Firebase Authentication トークン、FCM トークン、App Check トークンは、使用可能な場合、自動的にリクエストに追加されます。
- トリガーは、リクエスト本文を自動的に逆シリアル化し、認証トークンを検証します。
Firebase SDK for Cloud Functions(第 2 世代)以降は、次の各 Firebase クライアント SDK の最小バージョンとの連携によって、HTTPS 呼び出し可能関数をサポートします。
- Firebase SDK for Apple プラットフォーム 12.4.0
- Firebase SDK for Android 22.0.1
- Firebase Modular Web SDK v. 9.7.0
サポートされていないプラットフォーム上で構築されたアプリに同様の機能を追加する場合は、https.onCall のプロトコル仕様を参照してください。これ以降は、Apple プラットフォーム、Android、ウェブ、C++、Unity 用の HTTP 呼び出し可能関数の記述、デプロイ、呼び出し方法について説明します。
呼び出し可能関数を記述してデプロイする
このセクションのコード例は、サーバーサイド関数にリクエストを送信し、いずれかのクライアント SDK を使用してレスポンスを取得する方法を示す、完全なクイックスタート サンプルに基づいています。始めるには、まず必要なモジュールをインポートします。
Node.js
// Dependencies for callable functions.
const {onCall, HttpsError} = require("firebase-functions/https");
const {logger} = require("firebase-functions");
// Dependencies for the addMessage function.
const {getDatabase} = require("firebase-admin/database");
const sanitizer = require("./sanitizer");
Python
# Dependencies for callable functions.
from firebase_functions import https_fn, options
# Dependencies for writing to Realtime Database.
from firebase_admin import db, initialize_app
プラットフォーム(functions.https.onCall)または on_call のリクエスト ハンドラを使用して、HTTPS 呼び出し可能関数を作成します。このメソッドはリクエスト パラメータを受け取ります。
Node.js
// Saves a message to the Firebase Realtime Database but sanitizes the
// text by removing swearwords.
exports.addmessage = onCall((request) => {
  // ...
});
Python
@https_fn.on_call()
def addmessage(req: https_fn.CallableRequest) -> Any:
    """Saves a message to the Firebase Realtime Database but sanitizes the text
    by removing swear words."""
request パラメータには、クライアント アプリから渡されるデータと、認証状態などの追加コンテキストが含まれます。たとえば、Realtime Database にテキスト メッセージを保存する呼び出し可能関数の場合、data にメッセージ テキストが含まれ、auth に認証情報が含まれます。
Node.js
// Message text passed from the client.
const text = request.data.text;
// Authentication / user information is automatically added to the request.
const uid = request.auth.uid;
const name = request.auth.token.name || null;
const picture = request.auth.token.picture || null;
const email = request.auth.token.email || null;
Python
# Message text passed from the client.
text = req.data["text"]
# Authentication / user information is automatically added to the request.
uid = req.auth.uid
name = req.auth.token.get("name", "")
picture = req.auth.token.get("picture", "")
email = req.auth.token.get("email", "")
呼び出し可能関数のロケーションと呼び出し側のクライアントのロケーションが離れている場合、ネットワーク レイテンシが発生する可能性があります。パフォーマンスを最適化するには、適切な場所に関数のロケーションを指定し、クライアントサイドで SDK を初期化するときに、設定されたロケーションに応じて呼び出し可能関数のロケーションを調整します。
必要に応じて、App Check 証明書を添付して、請求詐欺やフィッシングなどの不正行為からバックエンド リソースを保護することができます。Cloud Functions で App Check の適用を有効にするをご覧ください。
結果を返送する
クライアントにデータを返送するには、JSON でエンコード可能なデータを返します。たとえば、加法演算の結果を返すには、次のように使用します。
Node.js
// returning result.
return {
  firstNumber: firstNumber,
  secondNumber: secondNumber,
  operator: "+",
  operationResult: firstNumber + secondNumber,
};
Python
return {
    "firstNumber": first_number,
    "secondNumber": second_number,
    "operator": "+",
    "operationResult": first_number + second_number
}
メッセージ テキストの例のサニタイズ済みテキストは、クライアントと Realtime Database の両方に返されます。Node.js では、JavaScript Promise を使用してこの処理を非同期で実行できます。
Node.js
// Saving the new message to the Realtime Database.
const sanitizedMessage = sanitizer.sanitizeText(text); // Sanitize message.
return getDatabase().ref("/messages").push({
  text: sanitizedMessage,
  author: {uid, name, picture, email},
}).then(() => {
  logger.info("New Message written");
  // Returning the sanitized message to the client.
  return {text: sanitizedMessage};
})
Python
# Saving the new message to the Realtime Database.
sanitized_message = sanitize_text(text)  # Sanitize message.
db.reference("/messages").push({  # type: ignore
    "text": sanitized_message,
    "author": {
        "uid": uid,
        "name": name,
        "picture": picture,
        "email": email
    }
})
print("New message written")
# Returning the sanitized message to the client.
return {"text": sanitized_message}
関数は値を返すか、Node.js の場合は値で解決される Promise を返す必要があります。そうしないと、データがクライアントに返送される前に関数が終了する可能性があります。詳しくは、関数を終了するをご覧ください。
ストリーミング結果の送受信
呼び出し可能関数には、ストリーミング結果を処理するメカニズムがあります。ストリーミングが必要なユースケースがある場合は、呼び出し可能なリクエストでストリーミングを構成し、クライアント SDK の適切なメソッドを使用して関数を呼び出すことができます。
ストリーミング結果を送信する
複数の個別の API リクエストや生成 AI API など、時間の経過とともに生成される結果を効率的にストリーミングするには、呼び出し可能リクエストの acceptsStreaming プロパティを確認します。このプロパティが true に設定されている場合、response.sendChunk() を使用して結果をクライアントにストリーミングできます。
たとえば、アプリで複数の地域の天気予報データを取得する必要がある場合、呼び出し可能関数を使用すると、ストリーミング レスポンスをリクエストしたクライアントに各地域の予報を個別に送信できるため、クライアントはすべての予報リクエストが解決するまで待機する必要がありません。
exports.getForecast = onCall(async (request, response) => { if (request.data?.locations?.length < 1) { throw new HttpsError("invalid-argument", "Missing locations to forecast"); } // fetch forecast data for all requested locations const allRequests = request.data.locations.map( async ({latitude, longitude}) => { const forecast = await weatherForecastApi(latitude, longitude); const result = {latitude, longitude, forecast}; // clients that support streaming will have each // forecast streamed to them as they complete if (request.acceptsStreaming) { response.sendChunk(result); } return result; }, ); // Return the full set of data to all clients return Promise.all(allRequests); });
response.sendChunk() の動作は、クライアントのリクエストの特定の詳細によって決まります。
- クライアントがストリーミング レスポンスをリクエストしている場合: - response.sendChunk(data)はデータをすぐに送信します。
- クライアントがストリーミング レスポンスをリクエストしていない場合: - response.sendChunk()はその呼び出しに対して何も行いません。すべてのデータの準備が整ったら、完全なレスポンスが送信されます。
クライアントがストリーミング レスポンスをリクエストしているかどうかを確認するには、request.acceptsStreaming プロパティを調べます。たとえば、request.acceptsStreaming が false の場合、クライアントは増分配信を想定していないため、個々のチャンクの準備や送信に関連する作業のようにリソースを大量に消費する作業をスキップできます。
ストリーミング結果を受け取る
一般的なシナリオでは、クライアントは .stream メソッドでストリーミングをリクエストしてから、結果を反復処理します。
Swift
func listenToWeatherForecast() async throws {
    isLoading = true
    defer { isLoading = false }
    Functions
      .functions(region: "us-central1")
    let getForecast: Callable<WeatherRequest, StreamResponse<WeatherResponse, [WeatherResponse]>> = Functions.functions().httpsCallable("getForecast")
    let request = WeatherRequest(locations: locations)
    let stream = try getForecast.stream(request)
    for try await response in stream {
      switch response {
      case .message(let singleResponse):
        weatherData["\(singleResponse.latitude),\(singleResponse.longitude)"] = singleResponse
      case .result(let arrayOfResponses):
        for response in arrayOfResponses {
          weatherData["\(response.latitude),\(response.longitude)"] = response
        }
        print("Stream ended.")
        return
      }
    }
  }
Web
// Get the callable by passing an initialized functions SDK.
const getForecast = httpsCallable(functions, "getForecast");
// Call the function with the `.stream()` method to start streaming.
const { stream, data } = await getForecast.stream({
  locations: favoriteLocations,
});
// The `stream` async iterable returned by `.stream()`
// will yield a new value every time the callable
// function calls `sendChunk()`.
for await (const forecastDataChunk of stream) {
  // update the UI every time a new chunk is received
  // from the callable function
  updateUi(forecastDataChunk);
}
// The `data` promise resolves when the callable
// function completes.
const allWeatherForecasts = await data;
finalizeUi(allWeatherForecasts);
次のように、stream 非同期イテラブルをループします。data プロミスを待機することで、リクエストの完了がクライアントに示されます。
Kotlin
// Get the callable by passing an initialized functions SDK.
val getForecast = functions.getHttpsCallable("getForecast");
// Call the function with the `.stream()` method and convert it to a flow
getForecast.stream(
  mapOf("locations" to favoriteLocations)
).asFlow().collect { response ->
  when (response) {
    is StreamResponse.Message -> {
      // The flow will emit a [StreamResponse.Message] value every time the
      // callable function calls `sendChunk()`.
      val forecastDataChunk = response.message.data as Map<String, Any>
      // Update the UI every time a new chunk is received
      // from the callable function
      updateUI(
        forecastDataChunk["latitude"] as Double,
        forecastDataChunk["longitude"] as Double,
        forecastDataChunk["forecast"] as Double,
      )
    }
    is StreamResponse.Result -> {
      // The flow will emit a [StreamResponse.Result] value when the
      // callable function completes.
      val allWeatherForecasts = response.result.data as List<Map<String, Any>>
      finalizeUI(allWeatherForecasts)
    }
  }
}
asFlow() 拡張機能関数を使用するには、org.jetbrains.kotlinx:kotlinx-coroutines-reactive ライブラリを依存関係としてアプリの build.gradle(.kts) ファイルに追加します。
Java
// Get the callable by passing an initialized functions SDK.
HttpsCallableReference getForecast = mFunctions.getHttpsCallable("getForecast");
getForecast.stream(
  new HashMap<String, Object>() {{
    put("locations", favoriteLocations);
  }}
).subscribe(new Subscriber<StreamResponse>() {
  @Override
  public void onSubscribe(Subscription subscription) {
    subscription.request(Long.MAX_VALUE);
  }
  @Override
  public void onNext(StreamResponse streamResponse) {
    if (streamResponse instanceof StreamResponse.Message) {
      // The flow will emit a [StreamResponse.Message] value every time the
      // callable function calls `sendChunk()`.
      StreamResponse.Message response = (StreamResponse.Message) streamResponse;
      Map<String, Object> forecastDataChunk =
        (Map<String, Object>) response.getMessage().getData();
      // Update the UI every time a new chunk is received
      // from the callable function
      updateUI(
        (double) forecastDataChunk.get("latitude"),
        (double) forecastDataChunk.get("longitude"),
        (double) forecastDataChunk.get("forecast")
      );
    } else if(streamResponse instanceof StreamResponse.Result) {
      // The flow will emit a [StreamResponse.Result] value when the
      // callable function completes.
      StreamResponse.Result response = (StreamResponse.Result) streamResponse;
      List<Map<String, Object>> allWeatherForecasts =
        (List<Map<String, Object>>) response.getResult().getData();
      finalizeUI();
    }
  }
  @Override
  public void onError(Throwable throwable) {
    // an error occurred in the function
  }
  @Override
  public void onComplete() {
  }
});
CORS(クロスオリジン リソース シェアリング)を構成する
cors オプションを使用して、関数にアクセスできるオリジンを制御します。
デフォルトでは、すべての送信元からのリクエストを許可するように、呼び出し可能関数には CORS が構成されています。一部のクロスオリジン リクエストを許可する場合は、許可するドメインまたは正規表現のリストを渡します。次に例を示します。
Node.js
const { onCall } = require("firebase-functions/v2/https");
exports.getGreeting = onCall(
  { cors: [/firebase\.com$/, "https://flutter.com"] },
  (request) => {
    return "Hello, world!";
  }
);
クロスオリジン リクエストを禁止するには、cors ポリシーを false に設定します。
エラーを処理する
有用なエラーの詳細をクライアントで取得できるようにするには、functions.https.HttpsError または https_fn.HttpsError のインスタンスをスローする(Node.js の場合は、このインスタンスで拒否された Promise を返す)ことにより、呼び出し可能関数からエラーを返します。エラーの code 属性は、gRPC のステータス コードに示されている値のいずれかになります。エラーには文字列 message も含まれています。デフォルトは空の文字列です。エラーでは、オプションの details フィールドに任意の値を設定することもできます。関数から HTTPS エラー以外のエラーがスローされると、クライアントでは代わりにメッセージ INTERNAL とコード internal が含まれるエラーを受け取ります。
たとえば、関数はデータ検証や認証に関するエラーをスローできますが、その際に呼び出し元のクライアントに返すエラー メッセージを付加できます。
Node.js
// Checking attribute.
if (!(typeof text === "string") || text.length === 0) {
  // Throwing an HttpsError so that the client gets the error details.
  throw new HttpsError("invalid-argument", "The function must be called " +
          "with one arguments \"text\" containing the message text to add.");
}
// Checking that the user is authenticated.
if (!request.auth) {
  // Throwing an HttpsError so that the client gets the error details.
  throw new HttpsError("failed-precondition", "The function must be " +
          "called while authenticated.");
}
Python
# Checking attribute.
if not isinstance(text, str) or len(text) < 1:
    # Throwing an HttpsError so that the client gets the error details.
    raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
                              message=('The function must be called with one argument, "text",'
                                       " containing the message text to add."))
# Checking that the user is authenticated.
if req.auth is None:
    # Throwing an HttpsError so that the client gets the error details.
    raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.FAILED_PRECONDITION,
                              message="The function must be called while authenticated.")
呼び出し可能関数をデプロイする
完成した呼び出し可能関数を index.js 内に保存した後、firebase deploy を実行すると、他のすべての関数とともにデプロイされます。呼び出し可能関数のみをデプロイするには、次に示すように --only 引数を使用して部分デプロイを行います。
firebase deploy --only functions:addMessage
関数のデプロイ時に権限エラーが発生した場合は、デプロイ コマンドを実行するユーザーに適切な IAM ロールが割り当てられていることを確認します。
クライアント開発環境を設定する
すべての前提条件を満たしていることを確認し、必要な依存関係とクライアント ライブラリをアプリに追加します。
iOS+
Firebase を Apple アプリに追加するの手順に沿って操作します。
Swift Package Manager を使用して Firebase の依存関係のインストールと管理を行います。
- Xcode でアプリのプロジェクトを開いたまま、[File] > [Add Packages] の順に移動します。
- プロンプトが表示されたら、Firebase Apple プラットフォーム SDK リポジトリを追加します。
- Cloud Functions ライブラリを選択します。
- ターゲットのビルド設定の [Other Linker Flags] セクションに -ObjCフラグを追加します。
- 上記の作業が完了すると、Xcode は依存関係の解決とバックグラウンドでのダウンロードを自動的に開始します。
https://github.com/firebase/firebase-ios-sdk.git
Web
- Firebase をウェブアプリに追加するの手順に沿って操作します。ターミナルから次のコマンドを実行します。npm install firebase@12.4.0 --save 
- Firebase Core と Cloud Functions の両方を手動で要求します。 - import { initializeApp } from 'firebase/app'; import { getFunctions } from 'firebase/functions'; const app = initializeApp({ projectId: '### CLOUD FUNCTIONS PROJECT ID ###', apiKey: '### FIREBASE API KEY ###', authDomain: '### FIREBASE AUTH DOMAIN ###', }); const functions = getFunctions(app); 
Android
- Android アプリに Firebase を追加するの手順に沿って操作します。 
- モジュール(アプリレベル)の Gradle ファイル(通常は - <project>/<app-module>/build.gradle.ktsまたは- <project>/<app-module>/build.gradle)に、Android 用 Cloud Functions ライブラリの依存関係を追加します。ライブラリのバージョニングの制御には、Firebase Android BoM を使用することをおすすめします。- dependencies { // Import the BoM for the Firebase platform implementation(platform("com.google.firebase:firebase-bom:34.4.0")) // Add the dependency for the Cloud Functions library // When using the BoM, you don't specify versions in Firebase library dependencies implementation("com.google.firebase:firebase-functions") } - Firebase Android BoM を使用すると、アプリは常に互換性のあるバージョンの Firebase Android ライブラリを使用します。 - (代替方法)BoM を使用せずに Firebase ライブラリの依存関係を追加する - Firebase BoM を使用しない場合は、依存関係の行でそれぞれの Firebase ライブラリのバージョンを指定する必要があります。 - アプリで複数の Firebase ライブラリを使用する場合は、すべてのバージョンの互換性を確保するため、BoM を使用してライブラリのバージョンを管理することを強くおすすめします。 - dependencies { // Add the dependency for the Cloud Functions library // When NOT using the BoM, you must specify versions in Firebase library dependencies implementation("com.google.firebase:firebase-functions:22.0.1") } 
クライアント SDK を初期化する
Cloud Functions のインスタンスを初期化します。
Swift
lazy var functions = Functions.functions()
Objective-C
@property(strong, nonatomic) FIRFunctions *functions;
// ...
self.functions = [FIRFunctions functions];
Web
const app = initializeApp({
  projectId: '### CLOUD FUNCTIONS PROJECT ID ###',
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
});
const functions = getFunctions(app);
Kotlin
private lateinit var functions: FirebaseFunctions // ... functions = Firebase.functions
Java
private FirebaseFunctions mFunctions; // ... mFunctions = FirebaseFunctions.getInstance();
関数を呼び出す
Swift
functions.httpsCallable("addMessage").call(["text": inputField.text]) { result, error in
  if let error = error as NSError? {
    if error.domain == FunctionsErrorDomain {
      let code = FunctionsErrorCode(rawValue: error.code)
      let message = error.localizedDescription
      let details = error.userInfo[FunctionsErrorDetailsKey]
    }
    // ...
  }
  if let data = result?.data as? [String: Any], let text = data["text"] as? String {
    self.resultField.text = text
  }
}
Objective-C
[[_functions HTTPSCallableWithName:@"addMessage"] callWithObject:@{@"text": _inputField.text}
                                                      completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) {
  if (error) {
    if ([error.domain isEqual:@"com.firebase.functions"]) {
      FIRFunctionsErrorCode code = error.code;
      NSString *message = error.localizedDescription;
      NSObject *details = error.userInfo[@"details"];
    }
    // ...
  }
  self->_resultField.text = result.data[@"text"];
}];
Web
var addMessage = firebase.functions().httpsCallable('addMessage');
addMessage({ text: messageText })
  .then((result) => {
    // Read result of the Cloud Function.
    var sanitizedMessage = result.data.text;
  });
Web
import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions();
const addMessage = httpsCallable(functions, 'addMessage');
addMessage({ text: messageText })
  .then((result) => {
    // Read result of the Cloud Function.
    /** @type {any} */
    const data = result.data;
    const sanitizedMessage = data.text;
  });
Kotlin
private fun addMessage(text: String): Task<String> { // Create the arguments to the callable function. val data = hashMapOf( "text" to text, "push" to true, ) return functions .getHttpsCallable("addMessage") .call(data) .continueWith { task -> // This continuation runs on either success or failure, but if the task // has failed then result will throw an Exception which will be // propagated down. val result = task.result?.data as String result } }
Java
private Task<String> addMessage(String text) { // Create the arguments to the callable function. Map<String, Object> data = new HashMap<>(); data.put("text", text); data.put("push", true); return mFunctions .getHttpsCallable("addMessage") .call(data) .continueWith(new Continuation<HttpsCallableResult, String>() { @Override public String then(@NonNull Task<HttpsCallableResult> task) throws Exception { // This continuation runs on either success or failure, but if the task // has failed then getResult() will throw an Exception which will be // propagated down. String result = (String) task.getResult().getData(); return result; } }); }
Dart
    final result = await FirebaseFunctions.instance.httpsCallable('addMessage').call(
      {
        "text": text,
        "push": true,
      },
    );
    _response = result.data as String;
C++
firebase::Future<firebase::functions::HttpsCallableResult> AddMessage(
    const std::string& text) {
  // Create the arguments to the callable function.
  firebase::Variant data = firebase::Variant::EmptyMap();
  data.map()["text"] = firebase::Variant(text);
  data.map()["push"] = true;
  // Call the function and add a callback for the result.
  firebase::functions::HttpsCallableReference doSomething =
      functions->GetHttpsCallable("addMessage");
  return doSomething.Call(data);
}
Unity
private Task<string> addMessage(string text) {
  // Create the arguments to the callable function.
  var data = new Dictionary<string, object>();
  data["text"] = text;
  data["push"] = true;
  // Call the function and extract the operation from the result.
  var function = functions.GetHttpsCallable("addMessage");
  return function.CallAsync(data).ContinueWith((task) => {
    return (string) task.Result.Data;
  });
}
クライアントでエラーを処理する
サーバーがエラーをスローした場合、または結果の Promise が拒否された場合、クライアントはエラーを受け取ります。
関数から返されたエラーの型が function.https.HttpsError の場合、クライアントにはサーバーエラーからエラーの code、message、details が出力されます。それ以外の場合、エラーにはメッセージ INTERNAL とコード INTERNAL が組み込まれます。呼び出し可能関数でエラーを処理する方法については、ガイダンスをご覧ください。
Swift
if let error = error as NSError? {
  if error.domain == FunctionsErrorDomain {
    let code = FunctionsErrorCode(rawValue: error.code)
    let message = error.localizedDescription
    let details = error.userInfo[FunctionsErrorDetailsKey]
  }
  // ...
}
Objective-C
if (error) {
  if ([error.domain isEqual:@"com.firebase.functions"]) {
    FIRFunctionsErrorCode code = error.code;
    NSString *message = error.localizedDescription;
    NSObject *details = error.userInfo[@"details"];
  }
  // ...
}
Web
var addMessage = firebase.functions().httpsCallable('addMessage');
addMessage({ text: messageText })
  .then((result) => {
    // Read result of the Cloud Function.
    var sanitizedMessage = result.data.text;
  })
  .catch((error) => {
    // Getting the Error details.
    var code = error.code;
    var message = error.message;
    var details = error.details;
    // ...
  });
Web
import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions();
const addMessage = httpsCallable(functions, 'addMessage');
addMessage({ text: messageText })
  .then((result) => {
    // Read result of the Cloud Function.
    /** @type {any} */
    const data = result.data;
    const sanitizedMessage = data.text;
  })
  .catch((error) => {
    // Getting the Error details.
    const code = error.code;
    const message = error.message;
    const details = error.details;
    // ...
  });
Kotlin
addMessage(inputMessage) .addOnCompleteListener { task -> if (!task.isSuccessful) { val e = task.exception if (e is FirebaseFunctionsException) { val code = e.code val details = e.details } } }
Java
addMessage(inputMessage) .addOnCompleteListener(new OnCompleteListener<String>() { @Override public void onComplete(@NonNull Task<String> task) { if (!task.isSuccessful()) { Exception e = task.getException(); if (e instanceof FirebaseFunctionsException) { FirebaseFunctionsException ffe = (FirebaseFunctionsException) e; FirebaseFunctionsException.Code code = ffe.getCode(); Object details = ffe.getDetails(); } } } });
Dart
try {
  final result =
      await FirebaseFunctions.instance.httpsCallable('addMessage').call();
} on FirebaseFunctionsException catch (error) {
  print(error.code);
  print(error.details);
  print(error.message);
}
C++
void OnAddMessageCallback(
    const firebase::Future<firebase::functions::HttpsCallableResult>& future) {
  if (future.error() != firebase::functions::kErrorNone) {
    // Function error code, will be kErrorInternal if the failure was not
    // handled properly in the function call.
    auto code = static_cast<firebase::functions::Error>(future.error());
    // Display the error in the UI.
    DisplayError(code, future.error_message());
    return;
  }
  const firebase::functions::HttpsCallableResult* result = future.result();
  firebase::Variant data = result->data();
  // This will assert if the result returned from the function wasn't a string.
  std::string message = data.string_value();
  // Display the result in the UI.
  DisplayResult(message);
}
// ...
// ...
  auto future = AddMessage(message);
  future.OnCompletion(OnAddMessageCallback);
  // ...
Unity
 addMessage(text).ContinueWith((task) => {
  if (task.IsFaulted) {
    foreach (var inner in task.Exception.InnerExceptions) {
      if (inner is FunctionsException) {
        var e = (FunctionsException) inner;
        // Function error code, will be INTERNAL if the failure
        // was not handled properly in the function call.
        var code = e.ErrorCode;
        var message = e.ErrorMessage;
      }
    }
  } else {
    string result = task.Result;
  }
});
推奨: App Check で不正行為を防止する
アプリをリリースする前に、App Check を有効にして、自分のアプリだけが呼び出し可能関数のエンドポイントにアクセスできるようにすることをおすすめします。