สร้างระบบ Waiting Room (คิวเข้าระบบ) สำหรับ Next.js โดยใช้ Vercel Edge Functions (Middleware) และ Upstash Redis จำกัดจำนวนผู้ใช้งานพร้อมกัน
Source Code: GitHub | Demo: Live Demo
Waiting Room คืออะไร?
จำกัดจำนวน Active Visitors เพื่อไม่ให้ Server Overload เหมาะสำหรับ:
- Flash Sale / Product Launch
- Ticket Booking
- High-traffic Events
ควบคุม 2 พารามิเตอร์:
- Max Capacity: จำนวนผู้ใช้พร้อมกันสูงสุด
- Session Timeout: วินาทีที่ยินยอมให้ User Idle ได้
Step 1: Project Setup
npx create-next-app@latest --typescript nextjs-waiting-room
cd nextjs-waiting-room
npm install @upstash/redis
Step 2: Implementation
Next.js Middleware (pages/api/_middleware.ts) จะ Intercept Request ทุกครั้งที่เข้ามา
import { Redis } from "@upstash/redis";
import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
const COOKIE_NAME_ID = "__waiting_room_id";
const COOKIE_NAME_TIME = "__waiting_room_last_update_time";
const UPSTASH_REDIS_REST_TOKEN = process.env.UPSTASH_REDIS_REST_TOKEN!;
const UPSTASH_REDIS_REST_URL = process.env.UPSTASH_REDIS_REST_URL!;
const TOTAL_ACTIVE_USERS = 10; // Max Concurrent Users
const SESSION_DURATION_SECONDS = 30; // Session Idle Timeout
const redis = new Redis({
url: UPSTASH_REDIS_REST_URL,
token: UPSTASH_REDIS_REST_TOKEN,
});
export async function middleware(req: NextRequest, ev: NextFetchEvent) {
const cookies = req.cookies;
let userId = cookies[COOKIE_NAME_ID];
// 1. สร้าง User ID ใหม่ถ้ายังไม่มี
if (!userId) {
userId = makeId(8);
}
// 2. เช็คจำนวน Active Sessions ใน Redis
const size = await redis.dbsize();
console.log("current capacity:", size);
// 3. มีที่ว่าง → เข้าได้เลย
if (size < TOTAL_ACTIVE_USERS) {
return getDefaultResponse(req, userId);
}
// 4. เต็มแล้ว → เช็คว่า User นี้มี Session อยู่หรือไม่
const user = await redis.get(userId);
if (user === "1") {
// มี Session อยู่ → เข้าได้
return getDefaultResponse(req, userId);
}
// 5. เต็ม และไม่มี Session → ส่งไป Waiting Room
return getWaitingRoomResponse();
}
function makeId(length: number): string {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
async function getDefaultResponse(request: NextRequest, userId: string) {
// สำหรับ Demo ใช้ Static HTML (จริงๆ ควร forward ไปหน้าจริง)
let newResponse = new NextResponse(defaultHtml);
newResponse.headers.set("content-type", "text/html;charset=UTF-8");
// อัปเดต Last Activity Time (Heartbeat)
const now = Date.now();
const lastUpdate = cookies[COOKIE_NAME_TIME];
let lastUpdateTime = lastUpdate ? parseInt(lastUpdate) : 0;
const diff = now - lastUpdateTime;
const updateInterval = (SESSION_DURATION_SECONDS * 1000) / 2;
if (diff > updateInterval) {
// Renew Session TTL ใน Redis
await redis.setex(userId, SESSION_DURATION_SECONDS, "1");
newResponse.cookies.set(COOKIE_NAME_TIME, now.toString());
}
newResponse.cookies.set(COOKIE_NAME_ID, userId);
return newResponse;
}
function getWaitingRoomResponse() {
return new NextResponse(waitingRoomHtml, {
headers: { "content-type": "text/html;charset=UTF-8" },
});
}
// HTML Templates
const waitingRoomHtml = `
Waiting Room
You are now in line.Thanks for your patience.
We are experiencing a high volume of traffic. Please sit tight and we will let you in soon.
This page will automatically refresh, please do not close your browser.
`;
const defaultHtml = `
Waiting Room Demo
Waiting Room Demo
Visit this site from a different browser, you will be forwarded to the waiting room when the capacity is full.
Check this project to set up a waiting room for your website.
`;
Step 3: Run & Deploy
Local Test
# ตั้ง TOTAL_ACTIVE_USERS = 1 ในโค้ด
npm run dev
# เปิด https://localhost:3000/api/hello ใน Browser หลายตัว
Deploy Vercel
vercel deploy --prod
Vercel จะรัน Middleware ที่ Edge Locations ทั่วโลก → Latency ต่ำสุด
สรุป
- Next.js Middleware + Upstash Redis = Waiting Room ที่รันบน Edge ได้
- Upstash REST API เข้ากันได้กับ Vercel Edge Functions (ไม่ต้อง TCP)
- DBSIZE Command ใช้เช็ค Capacity ได้เร็ว (O(1))
- Cookie-based Session + Redis TTL = Auto Cleanup
- ปรับแต่ง HTML/CSS ของ Waiting Room ได้ตาม Brand
More Examples: Upstash Redis Examples