第14章:スケジュール関数(Cronで定期実行)⏰
この章は「裏側に目覚まし時計をセットする回」だよ〜!⏰😆 毎日・毎時・毎週みたいな“定期処理”を、勝手に動くようにしていきます🤖✨
まず結論:スケジュール関数って何?🧠

onSchedule を使うと、決めた時刻や間隔で関数が自動実行されます⏰
中では Google Cloud の Cloud Scheduler が呼び出し役になって、指定タイミングで関数を起動します。(Firebase)
いつ使うの?(超実務あるある)📌
- 毎朝:前日の集計を作って Firestore に保存📊
- 毎時:古いデータ掃除🧹
- 毎週:ランキング再計算🏆
- 毎日:未読通知のまとめを作る📨
- 毎晩:AIで要約を作って翌朝表示🤖🌅
仕組みのイメージ(ここ大事)🧩

デプロイすると自動で👇が作られます:
- Cloud Scheduler の ジョブ
- 実行される HTTP関数(裏で呼ばれるやつ)
そして、ジョブが HTTP関数を起動する流れです。(Firebase) さらに、ジョブや関数をコンソールで手動で消したり弄ったりしないでね(壊れる可能性あり)という注意も公式で強めに書かれてます。(Firebase)
お金の感覚(ビビりポイントを先に潰す)💸🧯

スケジュール関数は「Cloud Schedulerのジョブ課金」が基本です。
- ジョブは $0.10 / 1ジョブ / 月(実行回数課金じゃない)💡
- 請求アカウントごとに月3ジョブは無料枠あり🎁 (プロジェクトごとじゃなく“アカウント単位”)(Google Cloud)
✅ 節約ワザ: 「毎朝7時にAもBもCもやる」なら、ジョブを増やさず1本にまとめて中で分岐するのが強いです💪😎
Cron(スケジュール文字列)の書き方🕰️
Cloud Scheduler は
- Unix Crontab
- App Engine 形式
どっちもOKです。(Firebase)
よく使う例👇
"every day 07:00":毎日7:00(わかりやすい)(Firebase)"0 7 * * *":毎日7:00(Cron)(Firebase)"*/10 * * * *":10分ごと(Cron)
タイムゾーン(日本はここをミスりがち)🌏🇯🇵

スケジュールは タイムゾーン指定できます。
ScheduleOptions.timeZone を使うと、そのタイムゾーン基準で実行されます。(Firebase)
例:"Asia/Tokyo" にすれば JST基準で毎朝7時に動く🎌✨
ハンズオン:毎朝レポートを Firestore に書く📝🔥

ここでは「毎朝7:00に、dailyReports/YYYY-MM-DD を作る」をやります☀️
さらに 二重実行でも壊れないように“ガード”も入れます🛡️
⚠️ 公式でも「前の実行が終わらないうちに次が走る可能性あるよ」と注意されています。(Firebase) なので「複数回動いてもOK(冪等)」が超重要です💡
1) functions/src/scheduled/dailyReport.ts を作る📄
import { onSchedule } from "firebase-functions/v2/scheduler";
import { logger } from "firebase-functions";
import { initializeApp } from "firebase-admin/app";
import { getFirestore, FieldValue } from "firebase-admin/firestore";
initializeApp();
const db = getFirestore();
const TZ = "Asia/Tokyo";
/** JSTで YYYY-MM-DD を作る(DateはだいたいUTCなので、表示だけJSTにする) */
function todayYmdInTimeZone(timeZone: string): string {
const dtf = new Intl.DateTimeFormat("en-CA", {
timeZone,
year: "numeric",
month: "2-digit",
day: "2-digit",
});
return dtf.format(new Date()); // 例: 2026-02-18
}
export const writeDailyReport = onSchedule(
{
// Cronでも "every day 07:00" でもOK
schedule: "0 7 * * *",
timeZone: TZ,
// 失敗時のリトライ(必要なときだけでOK)
retryCount: 3,
minBackoffSeconds: 10,
maxBackoffSeconds: 300,
maxDoublings: 3,
},
async (event) => {
const ymd = todayYmdInTimeZone(TZ);
const docId = ymd; // ここを固定にしておくと「二重実行」でも同じ場所に書ける
logger.info("writeDailyReport start", {
scheduleTime: event.scheduleTime, // 実行トリガー時刻(参考)
docId,
});
const ref = db.doc(`dailyReports/${docId}`);
try {
// create() は「既にあったら失敗」なので、二重実行ガードに使える👍
await ref.create({
createdAt: FieldValue.serverTimestamp(),
ymd,
message: "おはよう☀️ 今日もやっていこう💪",
scheduleTime: event.scheduleTime ?? null,
});
logger.info("writeDailyReport created", { docId });
} catch (e: any) {
// すでに作成済みならスキップ(=多重起動に強い)
const msg = String(e?.message ?? "");
if (msg.includes("ALREADY_EXISTS")) {
logger.warn("writeDailyReport already exists, skip", { docId });
return;
}
logger.error("writeDailyReport failed", e);
throw e; // throwすると Cloud Scheduler 側のリトライ対象になり得る
}
}
);
scheduleは Unix Crontab / AppEngine どっちでもOK。(Firebase)timeZoneで JST基準にできる。(Firebase)retryCountなどでリトライ設定も可能。(Firebase)
2) functions/src/index.ts から export する📦
export { writeDailyReport } from "./scheduled/dailyReport";
3) デプロイする🚀
firebase deploy --only functions
デプロイすると、スケジューラージョブ&HTTP関数が自動生成されます。(Firebase)
4) 手動で一回だけ実行してテストする▶️
Cloud Scheduler の画面から「今すぐ実行」できます(公式にも案内あり)。(Firebase)
→ 実行したら Firestore に dailyReports/YYYY-MM-DD が増えるか確認👀✨
つまずきポイント集(初心者がハマる所だけ)🧯
✅ 1) 「7時のつもりがズレる」😇
timeZone: "Asia/Tokyo"を付ける!(Firebase)- そして「日付文字列」は
Intl.DateTimeFormatで 表示だけJSTにするのが安全🎌
✅ 2) 「たまに2回動いてる気がする」😱

それ、仕様として起き得ます(次が前の実行中に走る可能性)。(Firebase)
➡️ 対策は 冪等(同じ処理が複数回動いてもOK)
今回みたいに docId 固定+ create() でガードはかなり強い🛡️✨
✅ 3) 「コンソールでジョブを消したら壊れた」💥
公式が「手動で削除/変更しないで」って言ってます。(Firebase) ➡️ 基本は コード直して deploy で管理しよう👍
AIを絡めて“朝レポ”を気持ちよくする🤖🌅

ここ、2026っぽく行こう✨
パターンA:スケジュール関数が「素材」を作り、フロントでAI整形🧠✨
- スケジュール関数:集計結果を Firestore に保存📊
- フロント(React):そのデータを読み、Firebase AI Logic で「読みやすい日本語」に整形して表示📱✨(Firebase)
- App Check と相性も良い(AI Logic はモバイル/WEB向けの設計)🧷(Firebase)
⚠️ なお、モデルの入れ替わりは起きます。 例として、Gemini 2.0 Flash / Flash-Lite が 2026-03-31 にリタイア予定と明記されています(新モデルへ移行推奨)。(Firebase)
パターンB:スケジュール関数の中でAI要約(サーバ側AI)🧠➡️🤖
「関数の中でAI処理」までやりたいなら、サーバ側AIは Genkit が王道ルートです(Firebase側でも案内あり)。(Firebase) (この教材だとAI本格編は後ろの章でガッツリやるイメージだね🔥)
Antigravity / Gemini CLI を“下ごしらえ職人”にする🛸🧰
- Gemini CLI の Firebase拡張は、Firebase MCP server を自動セットアップして、ドキュメント参照や作業補助を強化してくれます。(Firebase)
- MCP server は Antigravity や Gemini CLI など多くのクライアントで動くよ、と明記されています。(Firebase)
たとえばAIに👇みたいに頼むと便利です😆
- 「毎日7時JSTのCron式を提案して」⏰
- 「冪等な dailyReports 書き込みの雛形コード作って」🛡️
- 「失敗時リトライの設計を初心者向けに説明して」📚
※ただし最後は 必ず人間がレビューね!😎✍️(特に権限・コスト系)
ミニ課題(提出用)🎒✨
- 毎朝7:00に
dailyReports/YYYY-MM-DDを作る☀️ - 二重実行でも壊れないように
create()ガードを入れる🛡️ - 週1回(日曜3:00とか)で「30日より古い dailyReports を削除」する関数も追加🧹
- ジョブ数が増えすぎないよう、できるだけまとめる💸
この章のチェック(できたら勝ち)✅🏁
onScheduleが Cloud Scheduler で動くイメージを説明できる(Firebase)- Cron と “every day 07:00” の違いがわかる(Firebase)
timeZoneを付けてJST基準にできる(Firebase)- 「次が前の実行中に走る」可能性があるので、冪等にする理由を言える(Firebase)
- Cloud Scheduler の料金が「ジョブ課金」で、無料枠が3ジョブあることを知ってる(Google Cloud)
次の第15章は「ログ・エラー・アラート」🧯👀で、**“落ちても直せる人”**に進化する回だよ〜!😆