Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

สร้าง Waiting Room สำหรับ Next.js ด้วย Edge Functions + Upstash Redis

สร้างระบบ 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 พารามิเตอร์:

  1. Max Capacity: จำนวนผู้ใช้พร้อมกันสูงสุด
  2. 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