N

Nokfa Docs

Current: framework-next.js

ไม่มีชื่อบทความ

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
 └───────────────┘