Firebase Functions の onRequest と onCall の違い

目次
Firebase Cloud Functions で関数を書くとき、最初に決めるのが onRequest(HTTP関数) か onCall(callable関数) かです。どちらも HTTPS で呼べる関数を作りますが、「Firebase が何を肩代わりしてくれるか」がまったく違います。
個人アプリのバックエンドを Cloud Functions(TypeScript) で組んだとき、私は次の基準に落ち着きました。
- 基本は
onCall… アプリ内から呼ぶ、本人確認が要る処理(CRUD やユーザー操作)。認証・CORS を Firebase が自動処理してくれるので定型コードがゼロ onRequestを選ぶのは「外向き/特殊」なときだけ … ヘルスチェックなどの監視、外部サービスからの Webhook、レスポンスをストリーミングしたい処理
この記事では両者の違いを対比表とコードで整理し、この選定基準に至った理由を書きます。
2つの位置づけ
一言でいうとこうです。
onRequest= 生の HTTP 関数。Express 風の(req, res)が渡され、HTTPメソッド・認証・CORS・パース・レスポンスを全部自分で書く。URL を知っていれば誰でも叩けるonCall= callable 関数。Firebase が薄いプロトコルで包み、認証・CORS・App Check・入出力の JSON 変換を自動処理。基本は Firebase SDK から呼ぶ前提
onCall は「Firebase SDK を使うアプリのための、よくある処理を楽に書く」ための製品で、onRequest は「何でもありの生 HTTP」と捉えると分かりやすいです。
対比表
| 観点 | onRequest(HTTP) |
onCall(callable) |
|---|---|---|
| 呼び出し側 | 何でも(curl / fetch / 監視 / Webhook) | 基本 Firebase SDK(httpsCallable) |
| 引数 | (req, res) Express 風 |
(request) → request.data / request.auth |
| 認証 | 自前(Authorization ヘッダを検証) |
自動。request.auth にユーザー情報が入る |
| CORS | 自前 | 自動 |
| App Check | 自前検証 | 自動(強制もオプションで可) |
| 入出力形式 | 自由(任意の JSON / テキスト / バイナリ) | { data: ... } を送り、戻り値も自動で JSON 化 |
| エラー返却 | res.status(4xx/5xx) を自分で |
throw new HttpsError(...) → SDK 側で型付きエラーに |
| ストリーミング | SSE / chunked を自由に返せる | 対応あり(ただし後述の注意) |
| URL | 公開 URL を直接叩く | URL を意識せず関数名で呼ぶ |
| 向いてる用途 | 監視・Webhook・ストリーミング・外部連携 | アプリ内の CRUD・本人確認が要る処理 |
コードで見る違い
onRequest(自分で全部やる)
import { onRequest } from "firebase-functions/v2/https";
// 認証もCORSも自前。監視用のヘルスチェックのような公開エンドポイント向き
export const health = onRequest({ cors: true }, (req, res) => {
res.status(200).json({ status: "ok" });
});onCall(Firebase が包む)
import { onCall, HttpsError } from "firebase-functions/v2/https";
export const getItems = onCall((request) => {
// ここに来た時点で認証トークンは検証済み。
// ただし「未ログインを弾く」判断は自分でやる
if (!request.auth) {
throw new HttpsError("unauthenticated", "ログインが必要です");
}
const { keyword } = request.data; // クライアントが送ったデータ
return { items: findItems(keyword) }; // 自動で JSON 化されて返る
});フロントからの呼び出し(onCall は SDK 経由)
import { getFunctions, httpsCallable } from "firebase/functions";
const getItems = httpsCallable(getFunctions(), "getItems");
const res = await getItems({ keyword: "foo" });
console.log(res.data); // 関数の戻り値onRequest の方は普通に fetch("https://.../health") で叩けます。
本質は「何が自動か」
onCall が肩代わりしてくれるのは、ざっくり次の5つです。
- 認証トークンの検証 … クライアントの Firebase Auth トークンを検証し、
request.auth.uidに詰めてくれる - CORS … SDK 経由の呼び出しを想定して自動処理
- App Check … アプリの正当性チェック(有効化すれば自動で強制)
- 入出力の JSON エンベロープ …
{ data: ... }の詰め替えを自動化 - エラー整形 …
HttpsErrorを投げると、SDK 側でcode付きのエラーとして受け取れる
onRequest はこれらが一切ない生の HTTPです。自由度が高い代わりに、認証もCORSも自分で書きます。たとえば onRequest で本人確認をするなら、Authorization: Bearer <IDトークン> を受け取り、Admin SDK の verifyIdToken() で検証する処理を自前で書く必要があります。onCall ならこれが最初から済んでいる、というのが一番の差です。
注意:
onCallでも「未認証を弾く」判定は自分で書きます。request.authがundefinedでも関数自体は実行されるので、認証必須なら先頭で弾いてください。
エラーの返し方
地味に効くのがエラーの扱いです。
onRequest…res.status(429).json({ error: "..." })のように HTTP ステータスを自分で組み立てるonCall…throw new HttpsError("resource-exhausted", "レート上限です")と投げるだけ。クライアントの SDK 側ではerror.codeで分岐できる
HttpsError の code は gRPC 由来の決まった文字列(unauthenticated / permission-denied / not-found / resource-exhausted など)で、HTTP ステータスへのマッピングも Firebase が面倒を見てくれます。アプリ内エラーを型として扱いたいなら onCall が楽です。
ストリーミングの注意
チャットの逐次表示のようにレスポンスをストリーミングしたいことがあります。
onCall にもストリーミング応答の仕組みはあります(サーバ側でチャンクを送り、クライアントの httpsCallable(...).stream() で受ける)。ただし受信側の環境に制約がある場合は要注意です。とくに React Native の標準 fetch はレスポンスボディのストリーミング読み取りに対応しておらず、扱いづらい場面があります。
こういうときは、素直に onRequest で SSE(Server-Sent Events) や chunked を返す方が堅いです。生の HTTP なので受信側の選択肢が広く、環境に合わせたクライアントを使えます。「ストリーミングは onRequest 側に寄せる」と割り切ると設計がシンプルになります。
使い分けの判断基準
以上を踏まえた実務上の基準です。
| やりたいこと | どちら | 理由 |
|---|---|---|
| アプリ内の CRUD / ユーザー操作 | onCall | 本人確認が要る。認証・CORS 自動で定型コードが消える |
| 新規登録後のプロフィール作成など | onCall(or 認証トリガー) | 認証済み前提の処理 |
| ヘルスチェック等の監視エンドポイント | onRequest | 外部監視が叩く公開URL。SDK 前提でない |
| 外部サービスからの Webhook | onRequest | そもそも callable にできない |
| レスポンスのストリーミング | onRequest | SSE/chunked を自由に返せる |
原則はシンプルで、「アプリ内から本人確認付きで呼ぶ普通の処理は onCall、それ以外の“外向き・特殊”だけ onRequest」。まず onCall を既定にして、onRequest が要る理由(監視・Webhook・ストリーミング)が出てきたときだけ例外的に使う、という運用に落とすと迷いません。
まとめ
onRequestは生の HTTP。認証・CORS も含めて全部自前onCallは Firebase が認証・CORS・App Check・JSON変換・エラー整形を肩代わり。ただし SDK 前提- 認証が要るアプリ内処理は
onCall、監視・Webhook・ストリーミングはonRequest - 迷ったら「
onCallを既定、onRequestは例外」
参考ソース
関連記事

React Hook Form と Zod を zodResolver でつなぐ実装を、ログイン・新規登録フォームを題材に一気通貫でまとめます。z.infer での型導出、.refine でのパスワード一致、formState.errors の分割代入まで。さらに register の spread が非制御コンポーネントで効く理由と、Controller でラップしないと対応できないケースの境界を整理します。

pnpm製プロジェクトで、ターミナルのESLintは動くのにエディタ上では警告が一切出ない。原因はプラグインのhoist、決め手は拡張のログだった。.npmrcでの解決とモノレポでの設定まで。

TanStack Query を Next.js App Router の静的エクスポート構成に入れる手順を、Providerの初期化(use client)→layoutラップ→共通fetch(lib/api.ts)→hooks(1ファイル1フック)→コンポーネントでの使い方、まで一気通貫でまとめます。