・Firebase Console から プロジェクトを作成
・Firestore Database → データベースを作成
・プロジェクトの設定 → ウェブアプリに Firebase を追加 → configを保存。
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
npx create-next-app@latest
npm install firebase
1. src/app/chat/[roomId]/page.tsx
import { ChatRoom } from "@/features/ChatRoom";
type PageProps = {
params: {
roomId: string;
};
};
export default function ChatRoomPage({ params }: PageProps) {
const roomId = params.roomId;
if (!roomId) return <div>error</div>;
return <ChatRoom roomId={roomId} />;
}
2. src/common/firebase/firebaseConfig.ts
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};
const app = initializeApp(firebaseConfig);
export const firestoreDb = getFirestore(app);
3. src/features/ChatRoom.tsx
"use client";
import { firestoreDb } from "@/common/firebase/firebaseConfig";
import {
addDoc,
collection,
onSnapshot,
query,
Timestamp,
} from "firebase/firestore";
import { useRouter } from "next/navigation";
import { FC, useEffect, useState } from "react";
type Message = {
id: string;
text: string;
createdAt: Timestamp;
senderId: string;
username: string;
};
interface ChatRoomProps {
roomId: string;
}
export const ChatRoom: FC<ChatRoomProps> = ({ roomId }) => {
const router = useRouter();
const [messages, setMessages] = useState<Message[]>([]);
const [newMessage, setNewMessage] = useState("");
const [username, setUsername] = useState<string>("");
const [currentUser, setCurrentUser] = useState<string | null>(null);
useEffect(() => {
if (!roomId) return;
const q = query(collection(firestoreDb, `chatRooms/${roomId}/messages`));
const unsubscribe = onSnapshot(q, (querySnapshot) => {
const msgs: Message[] = [];
querySnapshot.forEach((doc) => {
const data = doc.data();
msgs.push({
id: doc.id,
text: data.text,
createdAt: data.createdAt,
senderId: data.senderId,
username: data.username,
});
});
setMessages(msgs);
});
return () => unsubscribe();
}, [roomId]);
const handleSetUsername = () => {
if (username.trim()) {
setCurrentUser(username);
}
};
const sendMessage = async () => {
if (!newMessage.trim() || !currentUser) return;
await addDoc(collection(firestoreDb, `chatRooms/${roomId}/messages`), {
text: newMessage,
createdAt: Timestamp.fromDate(new Date()),
senderId: "user123", // 実際のユーザーIDに置き換え
username: currentUser,
});
setNewMessage("");
};
return (
<div>
{!currentUser ? (
<div>
<h2>Set your username</h2>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<button onClick={handleSetUsername}>Set Username</button>
</div>
) : (
<div>
<h1>Chat Room: {roomId}</h1>
<div>
{messages.map((msg) => (
<div key={msg.id}>
<p>
<strong>{msg.username}:</strong> {msg.text}
</p>
<span>{msg.createdAt.toDate().toString()}</span>
</div>
))}
</div>
<input
type="text"
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
/>
<button onClick={sendMessage}>Send</button>
</div>
)}
</div>
);
};
4. src/common/types/env.d.ts
declare namespace NodeJS {
interface ProcessEnv {
readonly NEXT_PUBLIC_FIREBASE_API_KEY: string;
readonly NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN: string;
readonly NEXT_PUBLIC_FIREBASE_PROJECT_ID: string;
readonly NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET: string;
readonly NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID: string;
readonly NEXT_PUBLIC_FIREBASE_APP_ID: string;
readonly NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID: string;
}
}
5. .env.development
firebase設定を記述します