এই পোস্টে আমি ব্যাখ্যা করব কিভাবে Server-Sent Events (SSE) ব্যবহার করে Upstash Redis, Next.js Server Actions এবং Vercel-এ রিয়েল-টাইম নোটিফিকেশন বিল্ড করা হয়েছে। Upstash Redis-এ মেসেজ চ্যানেল আপনার অ্যাপ্লিকেশনের কমিউনিকেশন আর্কিটেকচারকে উল্লেখযোগ্যভাবে উন্নত করতে পারে, সেগুলোকে আরও রেসপন্সিভ এবং ডাইনামিক করে তোলা।
ডেমো
আমরা কী ব্যবহার করব
- Next.js (ফ্রন্টএন্ড এবং ব্যাকএন্ড)
- Upstash Redis (PUBLISH কমান্ড সহ Server-Sent Events)
- Tailwind CSS (স্টাইলিং)
- Vercel (ডিপ্লয়মেন্ট)
আপনার কী প্রয়োজন
- Node.js 18
- Upstash অ্যাকাউন্ট
- Vercel অ্যাকাউন্ট
Upstash Redis সেটআপ করা
Upstash অ্যাকাউন্টে লগ ইন করে Redis ট্যাবে একটি ডেটাবেস তৈরি করুন। Details ট্যাবে Connect your database সেকশন থেকে Redis URL কপি করে UPSTASH_REDIS_URL হিসেবে সংরক্ষণ করুন। REST API সেকশনে .env বাটনে ক্লিক করে UPSTASH_REDIS_REST_URL এবং UPSTASH_REDIS_REST_TOKEN কপি করুন।
প্রজেক্ট সেটআপ করা
রিপো ক্লোন করুন এবং টিউটোরিয়াল ফলো করুন:
git clone https://github.com/rishi-raj-jain/upstash-nextjs-publish-messages-with-sse-example cd upstash-nextjs-publish-messages-with-sse-example npm install
.env ফাইল তৈরি করুন:
# .env # উপরের ধাপগুলো থেকে প্রাপ্ত # Upstash Redis Secrets UPSTASH_REDIS_URL="rediss://default:...@...-...-...-....upstash.io:..." UPSTASH_REDIS_REST_URL="https://...-...-...-....upstash.io" UPSTASH_REDIS_REST_TOKEN="...="
নোট: UPSTASH_REDIS_URL এ "rediss" আছে (redis নয়), যাতে TLS/SSL অপশন ব্যবহার হয়।
লোকাল এনভায়রনমেন্ট চালান:
npm run dev
রিপোজিটরি কাঠামো
প্রজেক্টের প্রধান ফোল্ডার কাঠামো। লাল চিহ্নিত ফাইলগুলো এই পোস্টে আলোচনা করা হয়েছে:
- Upstash Redis-এ মেসেজ চ্যানেল বোঝা
- Next.js App Router-এ Server-Sent Events API তৈরি করা
- Next.js Server Actions সেটআপ করা Upstash Redis দিয়ে নোটিফিকেশন পাবলিশ করতে
- Next.js ফ্রন্টএন্ড সেটআপ করা SSE-এ دائمি লিসেন এবং রিয়েল-টাইমে নোটিফিকেশন ডিসপ্লে করতে
Upstash Redis-এ মেসেজ চ্যানেল বোঝা
Upstash Redis-এ publish/subscribe মডেল মেসেজ চ্যানেলের কোরে থাকে। পাবলিশার নামকৃত চ্যানেলে মেসেজ ব্রডকাস্ট করে, এবং সাবস্ক্রাইবার নির্দিষ্ট চ্যানেল থেকে রিয়েল-টাইমে মেসেজ রিসিভ করতে পারে। এই মডেল অ্যাপ্লিকেশনের বিভিন্ন পার্টের মধ্যে সিমলেস কমিউনিকেশন সক্ষম করে।
Edge-কম্প্যাটিবল লাইব্রেরি @upstash/redis ব্যবহার করে চ্যানেলে মেসেজ পাবলিশ করা:
import { Redis } from '@upstash/redis' const redis = Redis.fromEnv() await redis.publish('posts', JSON.stringify({ date: new Date().toString(), message: "I am a new message." }))
Node-কম্প্যাটিবল লাইব্রেরি ioredis ব্যবহার করে Upstash Redis চ্যানেল (এখানে posts) সাবস্ক্রাইব করা:
import Redis from 'ioredis' const redisSubscriber = new Redis(process.env.UPSTASH_REDIS_URL) const setKey = 'posts' redisSubscriber.subscribe(setKey, (err) => { if (err) console.log(err) }) redisSubscriber.on('message', (channel, message) => { if (channel === setKey) console.log(message) })
একটি চ্যানেলে মেসেজ পাবলিশ হলে সব সাবস্ক্রাইবার তা ইনস্ট্যান্টলি পায়, Upstash Redis-এ দক্ষ এবং রিয়েল-টাইম কমিউনিকেশন সক্ষম করে।
Next.js App Router-এ Server-Sent Events API তৈরি করা
Server-Sent Events (SSE) রিয়েল-টাইমে নতুন ডেটা পাঠানোর একটি পাওয়ারফুল উপায়, যাতে ক্লায়েন্টের একাধিক রিকোয়েস্টের প্রয়োজন নেই। ট্র্যাডিশনাল রিকোয়েস্ট-রেসপন্স মেকানিজমের বিপরীতে, SSE একটি সিঙ্গল, লং-লিভড HTTP কানেকশনওভার সার্ভার থেকে ক্লায়েন্টে আনডিরেকশনাল ডেটা ফ্লো সক্ষম করে।
Next.js App Router-এ SSE ইমপ্লিমেন্টেশন:
// File: app/api/stream/route.js export const dynamic = 'force-dynamic' export async function GET() { const encoder = new TextEncoder() const customReadable = new ReadableStream({ start(controller) { const message = 'Hey, I am a message.' controller.enqueue(encoder.encode(`data: ${message}\n\n`)) }, }) return new Response(customReadable, { headers: { 'Content-Type': 'text/event-stream; charset=utf-8', Connection: 'keep-alive', 'Cache-Control': 'no-cache, no-transform', 'Content-Encoding': 'none' }, }) }
Next.js Server Actions ব্যবহার করে Upstash Redis-এ মেসেজ পাবলিশ করা
Next.js Server Actions আপনাকে ফ্রন্টএন্ড কোডের ভিতরেই সার্ভার-সাইড লজিক ডিফাইন করতে দেয়, API Routes ম্যানুয়ালি তৈরি করার ঝামেলা এবং ফর্ম সাবমিশন স্ট্যাটাস ট্র্যাক করার ঝামেলা বাঁচায়।
use server ফাংশনের উপরে দিয়ে সেটি শুধু সার্ভার-সাইডে রান করবে তা নিশ্চিত করা হয়। ফর্ম সাবমিশন Server Action-এ ফর্ম থেকে message ভ্যালু এক্সট্র্যাক্ট করা হয়, Vercel Header থেকে ইউজারের কান্ট্রি বের করা হয় এবং তথ্যগুলো posts Upstash Redis মেসেজ চ্যানেলে পাবলিশ করা হয়।
// File: app/actions.jsx 'use server' import { Redis } from '@upstash/redis' import { headers } from 'next/headers' export async function publishNotification(formData) { 'use server' const redis = Redis.fromEnv() const message = formData.get('message') const headersList = headers() const country = headersList.get('x-vercel-ip-country') await redis.publish( 'posts', JSON.stringify({ message, country, date: new Date().toString(), })) }
এই Server Action ফর্ম সাবমিশন হলে আহ্বান করার জন্য ফর্মের action ইভেন্টে পাস করুন।
// File: app/page.jsx import { publishNotification } from './actions' const Home = () => { return ( <> <div> <form action={publishNotification} > {/* ক্লায়েন্ট সাইড ফর্ম কম্পোনেন্ট */} </form> </div> </> ) } export default Home
React-এর useFormStatus হুক ব্যবহার করে ফর্ম সাবমিশনের পেন্ডিং স্টেট ডিসপ্লে করা
নিচের কোড Next.js ফর্ম ক্লায়েন্ট সাইড কম্পোনেন্ট সেটআপ দেখায় যা ফর্ম সাবমিশন হ্যান্ডেল করে এবং React-এর useFormStatus হুক ব্যবহার করে pending স্টেট দেখায়।
- React-এর নতুন
useFormStatusহুক ইমপোর্ট করা হয়েছে, যা লাস্ট ফর্ম সাবমিশনের স্ট্যাটাস ইনফো দেয়। pendingরিয়েক্টিভ ভেরিয়েবল ফর্ম সাবমিশন প্রগ্রেসে আছে কিনা নির্দেশ করে।- সাবমিশন পেন্ডিং না হলে ফর্ম
resetকরা হয়। pendingবুলিয়ান ব্যবহার করে ফর্মের কন্ডিশনাল স্টেট দেখানো হয়।
'use client' import { useEffect } from 'react' import { useFormStatus } from 'react-dom' const Form = () => { const { pending } = useFormStatus() useEffect(() => { if (!pending) document.getElementById('publish-form').reset() }, [pending]) return ( <> <input placeholder="Your message" className="border rounded px-3 outline-none focus:border-black/50 py-2" type="text" name="message" required /> <button disabled={pending} className="hover:border-black/50 max-w-max border rounded py-1 px-3" type="submit" > {pending ? ( <div className="flex flex-row gap-x-2 items-center"> <div className="animate-spin border border-gray-800 rounded-full h-[15px] w-[15px]"></div> <span>Publishing</span> </div> ) : ( <>Publish Notification →</> )} </button> </> ) } export default Form
Next.js ফ্রন্টএন্ড সেটআপ করা Server-Sent Events دائمি লিসেন করার জন্য
এই সেকশনে আমরা SSE API মেসেজ লিসেন করার মিনিমাল লিসেনার এবং SSE API-তে কানেকশন পায়রসিস্ট করার অ্যাপরোচ শিখব।
React ফ্রন্টএন্ডে Server-Sent Events API লিসেন করা
React-এ ক্লায়েন্ট সাইড কম্পোনেন্টে SSE API লিসেন করতে useEffect হুক ব্যবহার করা হয়। /api/stream এন্ডপয়েন্টে পয়েন্ট করে একটি নতুন EventSource ইন্সট্যান্স তৈরি করে SSE API-এ কানেক্ট করা হয়। তারপর message ইভেন্টের জন্য ইভেন্ট লিসেনার অ্যাটাচ করা হয়, যেখানে স্ট্রিম থেকে আসা ডেটা JSON হিসেবে পার্স করা হয় এবং কম্পোনেন্টে প্রসেস বা ডিসপ্লে করা হয়।
কম্পোনেন্ট আনমাউন্ট হলে SSE কানেকশন ক্লোজ করার ক্লিনআপ ফাংশন থাকায় পটেনশিয়াল মেমরি লিক প্রতিরোধ হয়।
import { useEffect, useState } from 'react' const ClientSideComponent = () => { useEffect(() => { const eventSource = new EventSource('/api/stream') eventSource.addEventListener('message', (event) => { const tmp = JSON.parse(event.data) // প্রাপ্ত মেসেজ দিয়ে কিছু করুন }) return () => { eventSource.close() } }, []) return <></> } export default ClientSideComponent
React ফ্রন্টএন্ডে Server-Sent Events API-তে কানেকশন পায়রসিস্ট করা
Enhanced ভার্সনে আমরা এরর হ্যান্ডলিং এবং অটোমেটিক রিকানেকশন দিয়ে SSE API-এ কন্টিনিউয়াস এবং পায়রসিস্টেন্ট কানেকশন নিশ্চিত করেছি।
এটি connectToStream ফাংশন দ্বারা অর্জিত হয় যা SSE API-এ কানেকশন thiếtলাব এবং মেইনটেইন করার দায়িত্বে।
- ইনিশিয়াল কানেকশন এবং মেসেজ হ্যান্ডলিং:
ফাংশন একটি নতুন EventSource ইন্সট্যান্স তৈরি করে, /api/stream এন্ডপয়েন্টে কানেক্ট করে।
const connectToStream = () => { const eventSource = new EventSource('/api/stream') // .. }
আরও, message ইভেন্টের জন্য লিসেনার সেট করা হয়, যেখানে ইনকামিং ডেটা JSON হিসেবে পার্স করা হয়। পার্সড ডেটা React কম্পোনেন্টে প্রসেস বা ডিসপ্লে করা হয়।
const connectToStream = () => { // ... eventSource.addEventListener('message', (event) => { const tmp = JSON.parse(event.data) setPosts((prevPosts) => [...prevPosts, tmp]) }) // ... }
- এরর হ্যান্ডলিং এবং অটোমেটিক রিকানেকশন:
error ইভেন্টের জন্য এডিশনাল লিসেনার সেট করা হয়। যেকোনো এরর (যেমন কানেকশন ফেইল) হলে ইভেন্ট সোর্স ক্লোজ করা হয়। ক্লোজের পর setTimeout দিয়ে ১ মিলিসেকেন্ড विलম্বে রিকানেকশন ট্রিগার করা হয়। এই ছোট 딜ে স্মूद এবং কন্টিনিউয়াস রিকানেকশন প্রসেস নিশ্চিত করে সার্ভারকে র্যাপিড কানেকশন অ্যাটেম্প্ট দিয়ে ওভারওয়েল্ম না করে।
eventSource.addEventListener('error', () => { eventSource.close() setTimeout(connectToStream, 1) })
- SSE API সোর্স ক্লোজার হ্যান্ডলিং:
onclose ইভেন্ট ব্যবহার করে SSE API সোর্স ক্লোজ হওয়া ডিটেক্ট করা হয়। ক্লোজ হলে ফাংশন সংক্ষিপ্ত 딜ে পর আবার স্ট্রিমে কানেক্ট করার অটেম্প্ট শিডিউল করে।
eventSource.onclose = () => { setTimeout(connectToStream, 1) }
এই স্ট্র্যাটেজিগুলো কম্বাইন করে ফাংশন SSE API-এ কানেকশন পায়রসিস্টেন্ট রাখে। এরর বা ক্লোজার হওয়া সত্ত্বেও React কম্পোনেন্ট মিনিমাল 딜ে রিকানেক্ট করার চেষ্টা করবে, কার্যকরভাবে একটি কন্টিনিউয়াস কানেকশন মেইনটেইন করে।
Vercel-এ ডিপ্লয় করা
রিপোজিটরি Vercel-এ ডিপ্লয়ের জন্য রেডি। নিচের ধাপগুলো ফলো করুন:
- অ্যাপ কোড সহ
GitHub Repositoryতৈরি করুন - Vercel Dashboard-এ
New Projectতৈরি করুন - তैयার
GitHub Repositoryলিংক করুন নয় প্রজেক্ট হিসেবে - নিচে স্ক্রোল করে
Environment Variablesআপডেট করুন লোকাল.envথেকে - Deploy! 🚀