Next.js এর হাতেখড়ি সিরিজের এটাই শেষ পর্ব। এতদিন Routing, Server/Client Components, Data Fetching, Rendering কৌশল, Loading/Error States, API Routes, Server Actions, Middleware, Firebase ইন্টিগ্রেশন, Image/SEO, আর Deployment — সবকিছু আলাদাভাবে শিখেছি। আজ সব একসাথে জুড়ে একটা সম্পূর্ণ বুক-শেয়ারিং অ্যাপ বানাব — এতে React, Firebase, আর Cloudflare — তিনটা সিরিজেরই জ্ঞান একসাথে কাজে লাগবে।
আর্কিটেকচার — কোন পর্ব কোথায় প্রযোজ্য
- Firebase Auth (Firebase পর্ব ২-৩) — Email/Password লগইন
- সেশন কুকি (Next.js পর্ব ১০) — Firebase Auth-কে সার্ভারের সাথে জোড়া লাগানো
- Middleware (পর্ব ৯) —
/dashboardপ্রোটেক্ট করা - Firebase RTDB (Firebase পর্ব ৫-৮) — বইয়ের তথ্য, Security Rules সহ
- Server Component + fetch (পর্ব ৪-৫) — বইয়ের তালিকা SSR-এ দেখানো
- Server Actions (পর্ব ৮) — নতুন বই যোগ করার ফর্ম
- next/image ও Metadata (পর্ব ১১) — কভার ছবি ও SEO
- Cloudflare Workers (vinext) (পর্ব ১২) — লাইভ ডেপ্লয়মেন্ট
ফোল্ডার স্ট্রাকচার — সবকিছুর একটা মানচিত্র
app/
├── middleware.js # /dashboard প্রোটেক্ট
├── layout.js # রুট লেআউট
├── login/
│ └── page.js # Client Component — লগইন ফর্ম
├── api/
│ └── session/
│ └── route.js # সেশন কুকি সেট করা
├── dashboard/
│ ├── page.js # Server Component — বইয়ের তালিকা
│ └── actions.js # Server Actions — addBook, deleteBook
└── books/
└── [id]/
└── page.js # ডাইনামিক রুট — একটা বইয়ের বিস্তারিত
middleware.js — সব একসাথে
// middleware.js
import { NextResponse } from "next/server";
export function middleware(request) {
const sessionCookie = request.cookies.get("session");
if (!sessionCookie) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/dashboard/:path*"],
};
dashboard/actions.js — Server Actions
// app/dashboard/actions.js
"use server";
import { revalidatePath } from "next/cache";
export async function addBook(prevState, formData) {
const title = formData.get("title");
const author = formData.get("author");
if (!title || title.trim() === "") {
return { error: "বইয়ের নাম লিখতে হবে" };
}
await fetch(
"https://my-first-app-default-rtdb.firebasedatabase.app/books.json",
{
method: "POST",
body: JSON.stringify({ title, author, createdAt: Date.now() }),
}
);
revalidatePath("/dashboard"); // ক্যাশ রিফ্রেশ (পর্ব ৮)
return { success: true };
}
dashboard/page.js — Server Component + Server Action একসাথে
// app/dashboard/page.js
import Image from "next/image";
import { addBook } from "./actions";
import AddBookForm from "./AddBookForm"; // Client Component, নিচে
async function getBooks() {
const res = await fetch(
"https://my-first-app-default-rtdb.firebasedatabase.app/books.json",
{ cache: "no-store" } // SSR — সবসময় সাম্প্রতিক (পর্ব ৫)
);
return res.json();
}
export const metadata = {
title: "আমার বইয়ের তালিকা | ArifStack",
};
export default async function DashboardPage() {
const books = await getBooks();
return (
<div>
<h1>আমার বইয়ের তালিকা</h1>
<AddBookForm action={addBook} /> {/* Server Action পাস করা হচ্ছে */}
<div style={{ display: "flex", flexWrap: "wrap", gap: "12px" }}>
{Object.entries(books || {}).map(([id, book]) => (
<div key={id}>
<Image src={book.coverUrl || "/default-cover.jpg"} alt={book.title} width={100} height={150} />
<p>{book.title} — {book.author}</p>
</div>
))}
</div>
</div>
);
}
AddBookForm.js — useActionState দিয়ে Client Component
// app/dashboard/AddBookForm.js
"use client";
import { useActionState } from "react";
import { useFormStatus } from "react-dom";
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "সেভ হচ্ছে..." : "যোগ করো"}
</button>
);
}
export default function AddBookForm({ action }) {
const [state, formAction] = useActionState(action, null);
return (
<form action={formAction}>
<input name="title" placeholder="বইয়ের নাম" />
<input name="author" placeholder="লেখক" />
{state?.error && <p style={{ color: "red" }}>{state.error}</p>}
<SubmitButton />
</form>
);
}
এই একটা ফিচারেই (নতুন বই যোগ করা) এই সিরিজের প্রায় সব বড় ধারণা একসাথে কাজ করছে — Server Component পেজ, ভেতরে একটা Client Component ফর্ম (পর্ব ৩), সেই ফর্ম একটা Server Action কল করছে (পর্ব ৮), যা RTDB-তে লিখছে (Firebase পর্ব ৬), তারপর ক্যাশ রিফ্রেশ করছে (পর্ব ৫), আর পুরো পেজ Middleware দিয়ে প্রোটেক্টেড (পর্ব ৯)।
ইন্টারেক্টিভ — পুরো অ্যাপের ডেটা ফ্লো
নিচে ইউজার লগইন থেকে বই যোগ করা পর্যন্ত সম্পূর্ণ প্রক্রিয়া দেখো।
ডেপ্লয় করা — পর্ব ১২-এর সিদ্ধান্ত প্রয়োগ
এই অ্যাপে Middleware, Server Actions, SSR — সবকিছুই আছে, তাই পর্ব ১২-এর চেকলিস্ট অনুযায়ী এটার জন্য vinext + Cloudflare Workers দরকার, শুধু static export যথেষ্ট না।
npx vinext init npm run build npx wrangler deploy # সিক্রেট যোগ করা (পর্ব ১০, ১৩) # wrangler secret put FIREBASE_DB_SECRET
নিজে চেষ্টা করো
Exercise:
- উপরের ফোল্ডার স্ট্রাকচার নিজের প্রজেক্টে বানাও।
- Firebase Auth লগইন + সেশন কুকি + Middleware প্রোটেকশন একসাথে কাজ করাও (পর্ব ৯-১০)।
addBookServer Action বানাও, RTDB-তে লেখো,revalidatePathব্যবহার করো।useActionStateওuseFormStatusদিয়ে ফর্মে ভ্যালিডেশন ও লোডিং স্টেট যোগ করো।- পর্ব ১৩-এর প্রোডাকশন চেকলিস্ট দিয়ে যাচাই করো, তারপর vinext দিয়ে Cloudflare-এ ডেপ্লয় করো।
সিরিজ শেষ — তিন সিরিজ মিলিয়ে যা শিখলাম
React এর হাতেখড়ি (UI ও কম্পোনেন্ট), Firebase এর হাতেখড়ি (Auth ও Database), Cloudflare এর হাতেখড়ি (হোস্টিং ও ইনফ্রা), আর এখন Next.js এর হাতেখড়ি (এই তিনটাকে একটা সুসংগত ফ্রেমওয়ার্কে জোড়া লাগানো) — এই চারটা সিরিজ মিলিয়ে এখন একটা সম্পূর্ণ, আধুনিক, প্রোডাকশন-রেডি ওয়েব অ্যাপ্লিকেশন বানানোর সবগুলো টুকরো হাতে আছে। সামনের সেরা পদক্ষেপ হলো এই পুরো স্ট্যাক ব্যবহার করে নিজের একটা বাস্তব প্রজেক্ট আইডিয়া দাঁড় করানো।