ไม่มีชื่อบทความ
03_เชื่อม Firebase SDK บน Client‑Side และ Server‑Side ใน Next.js (เข้าใจ ‘Firebase Modular’)
📅 พร้อมใช้งานแล้ว!
หลังจากตั้งค่าทุกอย่างเรียบร้อย คุณสามารถเชื่อม Firestore กับแอป Next.js ได้ทันที เช่น ดึงข้อมูล messages มาแสดง หรือเพิ่มเอกสารใหม่ผ่าน form ได้ทันทีผ่าน Firebase SDK v10
หากต้องการตัวอย่างโค้ดการเชื่อมฝั่ง client/server ใน Next.js กดดูบทความ "Firebase SDK v10 + Next.js" ที่เกี่ยวข้องได้เลย!
🚀 ความแตกต่างระหว่าง Client และ Server ใน App Router (Next.js 15)
🔸 Client
- รันบนเบราว์เซอร์ (ฝั่งผู้ใช้)
- ใช้ Firebase SDK (
firebase/app,firebase/firestore,firebase/auth) - ต้องใช้ key ที่เริ่มต้นด้วย
NEXT_PUBLIC_ - เหมาะกับ: แสดงข้อมูล, เพิ่มเอกสารโดยผู้ใช้, interaction ทั่วไป
🔸 Server (เช่น Server Action หรือ API Route)
- รันฝั่งเซิร์ฟเวอร์ (Node.js runtime)
- ใช้ Firebase Admin SDK (
firebase-admin) - ใช้ Service Account (private key)
- เหมาะกับ: validate ข้อมูล, ป้องกัน bypass client, เขียน log, อ่าน/เขียนที่ต้องใช้สิทธิ์ระดับ admin
🔧 สร้างไฟล์เชื่อม Firebase
lib/firebaseClient.js
import { initializeApp, getApps, getApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
const config = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
};
const app = !getApps().length ? initializeApp(config) : getApp();
export const db = getFirestore(app);
lib/firebaseAdmin.js
import { initializeApp, cert, getApps, getApp } from 'firebase-admin/app';
import { getFirestore } from 'firebase-admin/firestore';
const adminConfig = {
credential: cert({
projectId: process.env.FIREBASE_ADMIN_PROJECT_ID,
clientEmail: process.env.FIREBASE_ADMIN_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_ADMIN_PRIVATE_KEY?.replace(/\\n/g, '\n'),
})
};
const adminApp = !getApps().length ? initializeApp(adminConfig) : getApp();
export const adminDB = getFirestore(adminApp);
🧪 ตัวอย่างหน้า Client (React)
'use client';
import { db } from '@/lib/firebaseClient';
import { collection, addDoc, getDocs } from 'firebase/firestore';
import { useEffect, useState } from 'react';
export default function Page() {
const [messages, setMessages] = useState([]);
useEffect(() => {
getDocs(collection(db, 'messages')).then(snapshot => {
setMessages(snapshot.docs.map(doc => doc.data()));
});
}, []);
const handleAdd = async () => {
await addDoc(collection(db, 'messages'), { text: 'New message' });
};
return (
<div>
<button onClick={handleAdd}>เพิ่มข้อความ</button>
<ul>
{messages.map((m, i) => <li key={i}>{m.text}</li>)}
</ul>
</div>
);
}
🌐 ตัวอย่าง Server Action (Route Handler)
// app/api/messages/route.js
import { NextResponse } from 'next/server';
import { adminDB } from '@/lib/firebaseAdmin';
export async function GET() {
const snap = await adminDB.collection('messages').get();
const data = snap.docs.map(doc => ({ id: doc.id, ...doc.data() }));
return NextResponse.json(data);
}
📊 Flow Diagram
User Request
│
┌─▼───────┐
│ Client │ ←→ firebaseClient.js
└─┬───────┘
│
▼ (Server Action / API Route)
┌─┴─────────────┐
│ Server (Node) │ ←→ firebaseAdmin.js
└───────────────┘