ในบทความนี้ ผมจะแนะนำการ implement rate limiting ใน web application ใช้ Vercel Edge Middleware และ library @upstash/ratelimit ตัวหลังใช้ Redis ที่ backend สำหรับ storage และ management ของ rate limit data
ข้อดีของการใช้ Vercel Edge
Vercel Edge เป็น computing platform ที่คำนวณที่ locations ใกล้ user มากที่สุด ผมจะใช้ Vercel Edge Middleware ที่ intercept requests ก่อนถึง backend ผมเชื่อมัน ideal สำหรับ rate limiting implementation หลายเหตุผล:
- Decoupled จาก backend บล็อก traffic ที่ edge location ก่อนถึง backend
- เร็ว ไม่มี cold start issues ทำให้ overhead น้อยที่สุด
- คุ้มค่ากว่า serverless functions
ทำไมถึง @upstash/ratelimit?
@upstash/ratelimitเป็น rate limiting library ที่ออกแบบและทดสอบมาเฉพาะสำหรับ edge functions- รองรับ multi-region Redis สำหรับ optimal latency จาก edge locations
- Upstash Redis เป็น managed Redis เพียงตัวที่เข้าถึงได้จาก edge functions ขอบคุณ REST API
มาเริ่ม implementation กัน:
Step 1: Redis Setup
สร้าง Redis database บน Upstash Console หรือ Upstash CLI ต้องการ UPSTASH_REDIS_REST_URL และ UPSTASH_REDIS_REST_TOKEN สำหรับขั้นตอนต่อไป

Step 2: Next.js Setup
สร้าง Next.js application (เช็ก ที่นี่ สำหรับ frameworks อื่น)
npx create-next-app@latest --typescript
ติดตั้ง @upstash/ratelimit:
npm i @upstash/ratelimit
สร้าง middleware.ts (top level ใน project folder):
import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const redis = new Redis({
url: "https://us1-merry-snake-32728.upstash.io",
token: "AX_sAdsdfsgODM5ZjExZGEtMmmVjNmE345445kGVmZTk5MzQ=",
});
const ratelimit = new Ratelimit({
redis: redis,
limiter: Ratelimit.slidingWindow(5, "10 s"),
});
export default async function middleware(
request: NextRequest,
event: NextFetchEvent,
): Promise<Response | undefined> {
const ip = request.ip ?? "127.0.0.1";
const { success, pending, limit, reset, remaining } = await ratelimit.limit(ip);
return success ? NextResponse.next() : NextResponse.redirect(new URL("/blocked", request.url));
}
export const config = {
matcher: "/",
};
อย่าลืมแทนที่ redis url และ token ของคุณ โค้ดใช้ algorithm slidingWindow อนุญาต 5 requests จาก IP เดียวใน 10 วินาที ถ้ามี property unique ต่อ user (userid, email ฯลฯ) ใช้แทน IP ได้ หาก request ถูก rate limited middleware จะ redirect ไป blocked page
สร้าง pages/blocked.tsx:
import styles from "@/styles/Home.module.css";
export default function Blocked() {
return (
<div>
<main className={styles.main}>
<h3>Access blocked.</h3>
</main>
</div>
);
}
แค่นี้! Deploy app ไป Vercel: vercel deploy
Refresh หน้า ควร redirect ไป blocked page หลัง 3 ครั้ง (หมายเหตุ: โค้ดตั้งไว้ 5 requests/10s แต่ข้อความบอก 3 ครั้ง อาจเป็น typo ในต้นฉบับ)
Less Remote Call ด้วย Caching
ไม่มีประสิทธิภาพถ้าทำ remote call ทุก request ฟีเจอร์ที่มีประโยชน์ของ @uptash/ratelimit package คือ cache data นานเท่าที่ edge function "hot" หมายถึง data ดึงจาก Redis เฉพาะตอน function "cold" ลด remote calls การ cache สร้างได้โดย declare ratelimit object นอก handler function middleware
Rate Limiting Algorithms
ตัวอย่างด้านบนใช้ sliding window algorithm ขึ้นอยู่กับความต้องการ ใช้ fixed window และ leaking bucket algorithms ได้ เช็ก ที่นี่
Multi Region Redis
สมเหตุสมผลที่จะมี multiple Redis databases ใน regions ต่างๆ เพื่อปรับปรุง performance จาก edge functions Config multiple region Redis ได้ตาม ที่อธิบายไว้