ANTIGRAVITY LABEN
記事一覧/Agents & Manager
Agents & Manager/2026-03-24中級

Antigravity エージェント × Firebase で リアルタイムチャットアプリを構築する実践ガイド

Antigravity の AI エージェントと Firebase Realtime Database を組み合わせて、認証・リアルタイム同期・プッシュ通知付きのチャットアプリを構築する完全ハンズオンガイド。

Antigravity338Firebase6リアルタイム3チャットアプリエージェント64プレミアム3

はじめに:なぜ Antigravity × Firebase なのか

リアルタイムチャットアプリは、モバイルアプリ開発の中でも特に人気の高いジャンルです。しかし、WebSocket の管理、認証フロー、プッシュ通知、データの永続化など、実装すべき要素が多く、従来は数週間の開発期間が必要でしました。

Antigravity の AI エージェントと Firebase を組み合わせることで、これらの複雑な実装を半自動化し、本格的なリアルタイムチャットアプリを短期間で構築できます。ここで扱うのはプロジェクトのセットアップから Firebase Authentication、Realtime Database の連携、そしてプッシュ通知の実装まで、一気通貫で解説します。

この記事の対象読者:

  • Antigravity の基本操作を理解している方
  • Firebase を使ったことがある、または興味がある方
  • リアルタイム機能を持つアプリを効率的に構築したい方

Firebase プロジェクトのセットアップ

まず、Firebase コンソールでプロジェクトを作成し、Antigravity から接続するための準備を行います。

Firebase CLI のインストールと初期化

# Firebase CLI のインストール
npm install -g firebase-tools
 
# ログイン
firebase login
 
# プロジェクト初期化(Realtime Database + Authentication + Cloud Messaging を選択)
firebase init
 
# 期待出力:
# ✔ Firebase initialization complete!
# - database: Firebase Realtime Database Rules
# - auth: Firebase Authentication

Antigravity プロジェクトでの Firebase 設定

Antigravity のエージェントに Firebase の設定情報を渡すために、agents.md ファイルにコンテキストを追加します:

<!-- agents.md -->
# Firebase Chat App Agent
 
## コンテキスト
- Firebase プロジェクト: my-chat-app-2026
- Realtime Database URL: https://my-chat-app-2026-default-rtdb.firebaseio.com
- 認証方式: Email/Password + Google Sign-In
- 対応プラットフォーム: Web (React)
 
## タスク
チャットアプリのフロントエンドとバックエンドを構築する。
Firebase Authentication で認証し、Realtime Database でメッセージをリアルタイム同期する。

Firebase Authentication の実装

チャットアプリの基盤となる認証機能を実装します。Antigravity のエージェントに「認証コンポーネントを作成して」と指示するだけで、以下のようなコードが生成されます。

認証フックの作成

// src/hooks/useAuth.ts
import { useState, useEffect } from "react";
import {
  getAuth,
  onAuthStateChanged,
  signInWithEmailAndPassword,
  createUserWithEmailAndPassword,
  signInWithPopup,
  GoogleAuthProvider,
  signOut,
  type User,
} from "firebase/auth";
import { getDatabase, ref, set, serverTimestamp } from "firebase/database";
 
export function useAuth() {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);
  const auth = getAuth();
  const db = getDatabase();
 
  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
      setUser(firebaseUser);
      setLoading(false);
 
      // ユーザープロフィールをDBに保存
      if (firebaseUser) {
        set(ref(db, `users/${firebaseUser.uid}`), {
          displayName: firebaseUser.displayName || "Anonymous",
          email: firebaseUser.email,
          photoURL: firebaseUser.photoURL,
          lastSeen: serverTimestamp(),
        });
      }
    });
    return unsubscribe;
  }, [auth, db]);
 
  const login = async (email: string, password: string) => {
    return signInWithEmailAndPassword(auth, email, password);
  };
 
  const register = async (email: string, password: string) => {
    return createUserWithEmailAndPassword(auth, email, password);
  };
 
  const loginWithGoogle = async () => {
    const provider = new GoogleAuthProvider();
    return signInWithPopup(auth, provider);
  };
 
  const logout = async () => {
    return signOut(auth);
  };
 
  return { user, loading, login, register, loginWithGoogle, logout };
}
// 期待出力: useAuth() が認証状態を管理し、ログイン/登録/ログアウト関数を提供

Realtime Database でメッセージを同期する

Firebase Realtime Database を使って、メッセージの送受信をリアルタイムで同期するコアロジックを実装します。

メッセージの送受信フック

// src/hooks/useChat.ts
import { useState, useEffect, useCallback } from "react";
import {
  getDatabase,
  ref,
  push,
  onChildAdded,
  query,
  orderByChild,
  limitToLast,
  serverTimestamp,
  off,
} from "firebase/database";
import type { User } from "firebase/auth";
 
interface Message {
  id: string;
  text: string;
  senderId: string;
  senderName: string;
  senderPhoto: string | null;
  timestamp: number;
}
 
export function useChat(roomId: string, user: User | null) {
  const [messages, setMessages] = useState<Message[]>([]);
  const db = getDatabase();
 
  useEffect(() => {
    if (!roomId) return;
 
    const messagesRef = query(
      ref(db, `rooms/${roomId}/messages`),
      orderByChild("timestamp"),
      limitToLast(100)
    );
 
    // 新しいメッセージをリアルタイムで受信
    const handleNewMessage = onChildAdded(messagesRef, (snapshot) => {
      const data = snapshot.val();
      const message: Message = {
        id: snapshot.key!,
        text: data.text,
        senderId: data.senderId,
        senderName: data.senderName,
        senderPhoto: data.senderPhoto,
        timestamp: data.timestamp,
      };
      setMessages((prev) => [...prev, message]);
    });
 
    return () => {
      off(messagesRef, "child_added", handleNewMessage);
    };
  }, [roomId, db]);
 
  const sendMessage = useCallback(
    async (text: string) => {
      if (!user || !text.trim()) return;
 
      const messagesRef = ref(db, `rooms/${roomId}/messages`);
      await push(messagesRef, {
        text: text.trim(),
        senderId: user.uid,
        senderName: user.displayName || "Anonymous",
        senderPhoto: user.photoURL,
        timestamp: serverTimestamp(),
      });
    },
    [user, roomId, db]
  );
 
  return { messages, sendMessage };
}
// 期待出力: リアルタイムでメッセージが同期され、送信関数が利用可能

チャット UI コンポーネント

// src/components/ChatRoom.tsx
import { useState, useRef, useEffect } from "react";
import { useAuth } from "../hooks/useAuth";
import { useChat } from "../hooks/useChat";
 
export function ChatRoom({ roomId }: { roomId: string }) {
  const { user } = useAuth();
  const { messages, sendMessage } = useChat(roomId, user);
  const [input, setInput] = useState("");
  const bottomRef = useRef<HTMLDivElement>(null);
 
  // 新しいメッセージが来たら自動スクロール
  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [messages]);
 
  const handleSend = async () => {
    if (!input.trim()) return;
    await sendMessage(input);
    setInput("");
  };
 
  return (
    <div className="chat-room">
      <div className="messages">
        {messages.map((msg) => (
          <div
            key={msg.id}
            className={`message ${msg.senderId === user?.uid ? "own" : "other"}`}
          >
            <img
              src={msg.senderPhoto || "/default-avatar.png"}
              alt={msg.senderName}
              className="avatar"
            />
            <div className="bubble">
              <span className="sender">{msg.senderName}</span>
              <p>{msg.text}</p>
              <time>{new Date(msg.timestamp).toLocaleTimeString("ja-JP")}</time>
            </div>
          </div>
        ))}
        <div ref={bottomRef} />
      </div>
      <div className="input-area">
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && handleSend()}
          placeholder="メッセージを入力..."
        />
        <button onClick={handleSend}>送信</button>
      </div>
    </div>
  );
}

セキュリティルールとプッシュ通知の設定

Firebase Realtime Database セキュリティルール

{
  "rules": {
    "rooms": {
      "$roomId": {
        "messages": {
          ".read": "auth != null",
          ".write": "auth != null",
          "$messageId": {
            ".validate": "newData.hasChildren(['text', 'senderId', 'senderName', 'timestamp'])
              && newData.child('senderId').val() === auth.uid
              && newData.child('text').val().length > 0
              && newData.child('text').val().length <= 1000"
          }
        }
      }
    },
    "users": {
      "$uid": {
        ".read": "auth != null",
        ".write": "auth.uid === $uid"
      }
    }
  }
}

このルールにより、認証済みユーザーのみがメッセージを読み書きでき、なりすまし送信も防止できます。メッセージは1000文字以内に制限しています。

Cloud Messaging でプッシュ通知

// src/services/notifications.ts
import { getMessaging, getToken, onMessage } from "firebase/messaging";
 
export async function setupNotifications() {
  const messaging = getMessaging();
 
  // 通知許可のリクエスト
  const permission = await Notification.requestPermission();
  if (permission !== "granted") {
    console.log("通知が拒否されました");
    return null;
  }
 
  // FCM トークンを取得
  const token = await getToken(messaging, {
    vapidKey: process.env.NEXT_PUBLIC_FIREBASE_VAPID_KEY,
  });
 
  // フォアグラウンドメッセージの受信
  onMessage(messaging, (payload) => {
    const { title, body } = payload.notification || {};
    if (title) {
      new Notification(title, { body, icon: "/icon-192.png" });
    }
  });
 
  return token;
}
// 期待出力: FCMトークンが取得され、プッシュ通知の受信が有効化

個人開発者の視点から(実体験メモ)

まとめ:エージェント駆動のアプリ開発で生産性を最大化する

ここで扱うのはAntigravity のエージェントと Firebase を組み合わせて、リアルタイムチャットアプリを構築する方法を解説しました。

  • Firebase Authentication: メール/パスワード + Google ログインによる認証
  • Realtime Database: メッセージのリアルタイム同期(onChildAdded
  • セキュリティルール: なりすまし防止とバリデーション
  • Cloud Messaging: プッシュ通知の実装

エージェントに適切なコンテキスト(agents.md)を与えることで、これらの複雑な機能を効率的に実装できます。Firebase の基本設定については「Firebase × Antigravity 連携ガイド」を、エージェントの高度な設定については「マルチエージェントオーケストレーション入門」もあわせてご覧ください。

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Agents & Manager2026-07-17
エージェントに渡した参照メモの7割は届いていませんでした — head で切る運用の限界を測った記録
定期実行のエージェントに参照メモを cat と head で渡していたところ、肝心な行が黙って落ちていました。切り取り位置を実測し、行数ではなくセクション単位の契約に置き換えるまでの記録です。
Agents & Manager2026-07-16
作業中のエージェントに、次の指示をいつ渡すか — 待つ・割り込む・積むを実運用で測りました
Antigravity v2.3.0 で入ったメッセージのキューイングと Send Now。待つ・割り込む・積むの三択を同じ物差しで測り、積んだ指示が古くなる問題と、前提スタンプで手戻りを22%から9%へ下げた記録をまとめました。
Agents & Manager2026-07-15
エージェントのコミットに .bak が混じる — 自動修正ツールの副産物がリポジトリに入る原因と対処
エージェントに --fix 系ツールを走らせた後のコミットに .bak や .orig が紛れ込む症状について、再現条件と原因、ステージ範囲の限定・副産物の即時掃除・pre-commit での拡張子ゲートという3つの対処をまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →