はじめに:なぜ 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 AuthenticationAntigravity プロジェクトでの 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 連携ガイド」を、エージェントの高度な設定については「マルチエージェントオーケストレーション入門」もあわせてご覧ください。