Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

アプリから関数を呼び出す

Cloud Functions for Firebase のクライアント SDK を使用すると、Firebase アプリから関数を直接呼び出すことができます。この方法でアプリから関数を呼び出すには、Cloud Functions において HTTPS 呼び出し可能関数を記述してデプロイし、アプリから関数を呼び出すためのクライアント ロジックを追加します。

HTTPS 呼び出し可能関数は HTTP 関数と類似しているものの、同一ではないことに注意することが重要です。HTTPS 呼び出し可能関数を使用するには、プラットフォームのクライアント SDK を functions.https バックエンド API とともに使用する(またはプロトコルを実装する)必要があります。呼び出し可能関数と HTTP 関数の主な違いは以下のとおりです。

  • 呼び出し可能関数では、Firebase Authentication トークン、FCM トークン、App Check トークンが利用可能な場合、自動的にリクエストに追加されます。
  • functions.https.onCall トリガーは、リクエスト本文を自動的に逆シリアル化し、認証トークンを検証します。

Firebase SDK for Cloud Functions v0.9.1 以降は、次の各 Firebase クライアント SDK の最小バージョンとの連携によって、HTTPS 呼び出し可能関数をサポートします。

  • Firebase SDK for iOS 8.8.0
  • Firebase SDK for Android 20.0.1
  • Firebase JavaScript SDK 8.10.0
  • Firebase Modular Web SDK v. 9.0

サポートされていないプラットフォーム上で構築されたアプリに同様の機能を追加する場合は、https.onCall のプロトコル仕様を参照してください。これ以降は、iOS、Android、ウェブ、C++、Unity 用の HTTPS 呼び出し可能関数の記述、デプロイ、呼び出し方法について説明します。

呼び出し可能関数の記述とデプロイ

functions.https.onCall を使用して HTTPS 呼び出し可能関数を作成します。このメソッドは、data とオプションの context の 2 つのパラメータを受け取ります。

// Saves a message to the Firebase Realtime Database but sanitizes the text by removing swearwords.
exports.addMessage = functions.https.onCall((data, context) => {
  // ...
});

たとえば、テキスト メッセージを Realtime Database に保存する呼び出し可能関数の場合、data にはメッセージ テキストが含まれ、context パラメータはユーザーの認証情報を表します。

// Message text passed from the client.
const text = data.text;
// Authentication / user information is automatically added to the request.
const uid = context.auth.uid;
const name = context.auth.token.name || null;
const picture = context.auth.token.picture || null;
const email = context.auth.token.email || null;

呼び出し可能関数のロケーションと呼び出し側のクライアントのロケーションが離れている場合、ネットワーク レイテンシが発生する可能性があります。パフォーマンスを最適化するには、適切な場所に関数のロケーションを指定し、クライアント側で SDK を初期化するときに、設定されたロケーションに応じて呼び出し可能関数のロケーションを調整します。

必要に応じて、App Check 証明書を添付して、請求詐欺やフィッシングなどの不正行為からバックエンド リソースを保護することができます。Cloud Functions に対して App Check の適用を有効にするをご覧ください。

結果の返送

クライアントにデータを返送するには、JSON でエンコード可能なデータを返します。たとえば、付加オペレーションの結果を返すには、次のように使用します。

// returning result.
return {
  firstNumber: firstNumber,
  secondNumber: secondNumber,
  operator: '+',
  operationResult: firstNumber + secondNumber,
};

非同期オペレーションの後にデータを返すには、Promise を返します。Promise によって返されたデータは、クライアントに返送されます。たとえば、呼び出し可能関数が Realtime Database に書き込んだサニタイズ済みのテキストを返すことができます。

// Saving the new message to the Realtime Database.
const sanitizedMessage = sanitizer.sanitizeText(text); // Sanitize the message.
return admin.database().ref('/messages').push({
  text: sanitizedMessage,
  author: { uid, name, picture, email },
}).then(() => {
  console.log('New Message written');
  // Returning the sanitized message to the client.
  return { text: sanitizedMessage };
})

エラーを処理する

有用なエラーの詳細をクライアントで取得できるようにするには、functions.https.HttpsError のインスタンスをスローする(またはこのインスタンスで拒否された Promise を返す)ことにより、呼び出し可能関数からエラーを返します。エラーの code 属性は、functions.https.HttpsError に示されているいずれかの値を取ることができます。エラーには文字列 message も含まれています。デフォルトは空の文字列です。エラーでは、オプションの details フィールドに任意の値を設定することもできます。関数から HttpsError 以外のエラーがスローされると、クライアントでは代わりにメッセージ INTERNAL とコード internal が含まれるエラーを受け取ります。

たとえば、関数はデータ検証や認証に関するエラーをスローすることができますが、その際に呼び出し元のクライアントに返すエラー メッセージを付加することができます。

// Checking attribute.
if (!(typeof text === 'string') || text.length === 0) {
  // Throwing an HttpsError so that the client gets the error details.
  throw new functions.https.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 (!context.auth) {
  // Throwing an HttpsError so that the client gets the error details.
  throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
      'while authenticated.');
}

呼び出し可能関数をデプロイする

完成した呼び出し可能関数を index.js 内に保存した後、firebase deploy を実行すると、他のすべての関数とともにデプロイされます。呼び出し可能関数のみをデプロイするには、次に示すように --only 引数を使用して部分デプロイを行います。

$ firebase deploy --only functions:addMessage

関数のデプロイ時に権限エラーが発生した場合は、デプロイ コマンドを実行するユーザーに適切な IAM ロールが割り当てられていることを確認します。

クライアント開発環境を設定する

すべての前提条件を満たしていることを確認し、必要な依存関係とクライアント ライブラリをアプリに追加します。

iOS

  1. Firebase を iOS アプリに追加するの手順に沿って操作します。
  2. Cloud Functions ポッドを Podfile に追加します。
    pod 'Firebase/Functions'
  3. ファイルを保存して、次のコマンドを実行します。
    pod install

ウェブ バージョン 9

  1. Firebase をウェブアプリに追加するの手順に沿って操作します。ターミナルから次のコマンドを実行します。
    npm install firebase@9.1.2 --save
    
  2. Firebase Core と Cloud Functions の両方を手動で require します。

     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);
    

ウェブ バージョン 8

  1. Firebase をウェブアプリに追加するの手順に沿って操作します。
  2. Firebase Core と Cloud Functions のクライアント ライブラリをアプリに追加します。
    <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-functions.js"></script>
    

Cloud Functions SDK は、npm パッケージとしても提供されています。

  1. ターミナルから次のコマンドを実行します。
    npm install firebase@8.10.0 --save
    
  2. Firebase Core と Cloud Functions の両方を手動で require します。
    const firebase = require("firebase");
    // Required for side-effects
    require("firebase/functions");
    

Java

  1. Android アプリに Firebase を追加するの手順に沿って操作します。

  2. Firebase Android BoM を使用して、モジュール(アプリレベル)の Gradle ファイル(通常は app/build.gradle)で Cloud Functions Android ライブラリの依存関係を宣言します。

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:28.4.2')
    
        // Declare 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 {
        // Declare 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:20.0.1'
    }
    

Kotlin+KTX

  1. Android アプリに Firebase を追加するの手順に沿って操作します。

  2. Firebase Android BoM を使用して、モジュール(アプリレベル)の Gradle ファイル(通常は app/build.gradle)で Cloud Functions Android ライブラリの依存関係を宣言します。

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:28.4.2')
    
        // Declare 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-ktx'
    }
    

    Firebase Android BoM を使用すると、アプリは常に互換性のあるバージョンの Firebase Android ライブラリを使用します。

    (別の方法)BoM を使用せずに Firebase ライブラリの依存関係を宣言する

    Firebase BoM を使用しない場合は、依存関係の行でそれぞれの Firebase ライブラリのバージョンを指定する必要があります。

    アプリで複数の Firebase ライブラリを使用する場合は、すべてのバージョンの互換性を確保するため、ライブラリのバージョンの管理に BoM を使用することを強くおすすめします。

    dependencies {
        // Declare 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-ktx:20.0.1'
    }
    

C++

Android の C++ の場合:

  1. Firebase を C++ プロジェクトに追加するの手順に沿って操作します。
  2. firebase_functions ライブラリを CMakeLists.txt ファイルに追加します。

iOS の C++ の場合:

  1. Firebase を C++ プロジェクトに追加するの手順に沿って操作します。
  2. Cloud Functions ポッドを Podfile に追加します。
    pod 'Firebase/Functions'
  3. ファイルを保存して、次のコマンドを実行します。
    pod install
  4. Firebase C++ SDK の Firebase Core と Cloud Functions フレームワークを Xcode プロジェクトに追加します。
    • firebase.framework
    • firebase_functions.framework

Unity

  1. Firebase を Unity プロジェクトに追加するの手順に沿って操作します。
  2. Firebase Unity SDKFirebaseFunctions.unitypackage を Unity プロジェクトに追加します。

クライアント SDK を初期化する

Cloud Functions のインスタンスを初期化します。

Swift

lazy var functions = Functions.functions()

Objective-C

@property(strong, nonatomic) FIRFunctions *functions;
// ...
self.functions = [FIRFunctions functions];

ウェブ バージョン 8

firebase.initializeApp({
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
  projectId: '### CLOUD FUNCTIONS PROJECT ID ###'
  databaseURL: 'https://### YOUR DATABASE NAME ###.firebaseio.com',
});

// Initialize Cloud Functions through Firebase
var functions = firebase.functions();

ウェブ バージョン 9

const app = initializeApp({
  projectId: '### CLOUD FUNCTIONS PROJECT ID ###',
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
});
const functions = getFunctions(app);

Java

private FirebaseFunctions mFunctions;
// ...
mFunctions = FirebaseFunctions.getInstance();

Kotlin+KTX

private lateinit var functions: FirebaseFunctions
// ...
functions = Firebase.functions

C++

firebase::functions::Functions* functions;
// ...
functions = firebase::functions::Functions::GetInstance(app);

Unity

functions = Firebase.Functions.DefaultInstance;

関数を呼び出す

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 == FIRFunctionsErrorDomain) {
      FIRFunctionsErrorCode code = error.code;
      NSString *message = error.localizedDescription;
      NSObject *details = error.userInfo[FIRFunctionsErrorDetailsKey];
    }
    // ...
  }
  self->_resultField.text = result.data[@"text"];
}];

ウェブ バージョン 8

var addMessage = firebase.functions().httpsCallable('addMessage');
addMessage({ text: messageText })
  .then((result) => {
    // Read result of the Cloud Function.
    var sanitizedMessage = result.data.text;
  });

ウェブ バージョン 9

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;
  });

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;
                }
            });
}

Kotlin+KTX

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
            }
}

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 の場合、クライアントにはサーバーエラーからエラーの codemessagedetails が出力されます。それ以外の場合、エラーにはメッセージ 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 == FIRFunctionsErrorDomain) {
    FIRFunctionsErrorCode code = error.code;
    NSString *message = error.localizedDescription;
    NSObject *details = error.userInfo[FIRFunctionsErrorDetailsKey];
  }
  // ...
}

ウェブ バージョン 8

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;
    // ...
  });

ウェブ バージョン 9

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;
    // ...
  });

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();
                    }

                    // ...
                }

                // ...
            }
        });

Kotlin+KTX

addMessage(inputMessage)
        .addOnCompleteListener(OnCompleteListener { task ->
            if (!task.isSuccessful) {
                val e = task.exception
                if (e is FirebaseFunctionsException) {
                    val code = e.code
                    val details = e.details
                }

                // ...
            }

            // ...
        })

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 を有効にして、自分のアプリだけが呼び出し可能関数のエンドポイントにアクセスできるようにする必要があります。