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

สร้าง Quota System เชื่อถือได้สำหรับ SaaS ด้วย Upstash และ Next.js

ในโพสต์นี้ เราจะแสดงวิธีใช้ Upstash สร้าง quota system สำหรับ SaaS application ที่ทำด้วย Next.js และ Prisma เราจะใช้ Next.js API routes สร้าง simple API

ใน SaaS application หลายแห่ง คุณอาจเคยเจอ quota system ระบบที่จำกัดจำนวน actions ที่ user ทำได้ในเวลาที่กำหนด

ในตัวอย่างของเรา user สามารถส่ง 1,000 API requests ต่อเดือน บนแผน "Free" เท่านั้น หาก user พยายามส่งเกิน 1,000 requests แอปพลิเคชันจะบล็อก user ไม่ให้ส่ง requests เพิ่ม

API requests เหล่านี้ใช้ดึงเนื้อหา spreadsheet แปลงเป็น JSON นี่คือสิ่งที่ fastsheet ทำ แปลง Google Sheets spreadsheet ใดๆ ให้เป็น JSON API

สร้าง Quota System เชื่อถือได้สำหรับ SaaS ด้วย Upstash และ Next.js

กำหนด Database Schema

ตามที่กล่าว เราจะใช้ Prisma เป็น ORM โต้ตอบกับ database นี่คือตัวอย่าง User model ที่ implement quota system รวมถึง Spreadsheet model เก็บ spreadsheets:

model User {
  id        Int      @id @default(autoincrement())
  planId    String   @default("FREE")
  email     String   @unique
  quota     Int      @default(0)
  spreadsheets Spreadsheet[]
}

model Spreadsheet {
  id        Int        @id @default(autoincrement())
  userId    Int
  user      User       @relation(fields: [userId], references: [id])
  content   String
}
  • Field planId คือแผนที่ user subscribe
  • Field quota คือจำนวน API requests ที่ user ทำในเดือนปัจจุบัน
  • Model Spreadsheet แทน spreadsheet ที่เชื่อมต่อกับ User
  • User หนึ่งมี Spreadsheet หลายตัว link เข้าไปได้

เพื่อให้เรียบง่าย เราจะจัดการแค่แผน "FREE" ที่อนุญาต 1,000 API requests ต่อเดือน

เพิ่ม Quota ของ User

เราอยากเพิ่ม quota ของ user ทุกครั้งที่ user ส่ง API request

ต nhìnดูง่ายและตรงไปตรงมา เราสามารถเพิ่ม quota ของ user ทีละ 1 ทุกครั้งที่ user ส่ง API request บน Next.js API route:

// pages/api/spreadsheets/[id]/index.ts
import type { NextApiRequest, NextApiResponse } from "next";

const MAX_FREE_TIER_QUOTA = 1000;

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  // ดึง ID ของ spreadsheet จาก URL
  const { id } = req.params;
  
  // ดึง spreadsheet และ user ที่เกี่ยวข้องจาก database
  const spreadsheet = await prisma.spreadsheet.findUnique({
    where: { id: Number(id) },
    include: { user: true },
  });
  
  // Verify spreadsheet exists
  if (!spreadsheet) {
    return res.status(404).json({ error: "Spreadsheet not found" });
  }
  
  // Verify user not over quota
  if (spreadsheet.user.quota >= MAX_FREE_TIER_QUOTA) {
    return res.status(429).json({ error: "Quota exceeded" });
  }
  
  // เพิ่ม quota ของ user ทีละ 1
  await prisma.user.update({
    where: { id: spreadsheet.user.id },
    data: { quota: { increment: 1 } },
  });
  
  // ส่งคืนเนื้อหา spreadsheet
  return res.json({ content: spreadsheet.content });
}

แต่มีปัญหากับวิธีนี้ ไม่เร็วเท่าที่ควร

เราสร้าง database transaction เพื่อเพิ่ม quota ของ user บนทุก API request

เราเข้าถึง database ได้ quota ของ user สร้าง transaction increment quota ทีละ 1 แล้วส่ง spreadsheet content

สิ่งนี้ไม่ optimal หลายเหตุผล:

  • Database ของคุณอาจช้า จะทำให้ response time ช้า
  • Database อาจอยู่ไกลจาก server อีกครั้ง response time ช้า
  • โค้ดนี้ยากที่จะทำให้ Edge compatible ต้องสร้าง multiple database replicas รอบโลก (ไม่ใช่งานง่าย!)

ใช้ Upstash เพิ่ม Quota ของ User

เพื่อแก้ปัญหานี้ เราจะใช้ Upstash Redis® เพิ่ม quota ของ user Upstash Redis® เป็น Redis® database ที่เร็วและน่าเชื่อถือ host บน cloud สำหรับ serverless environments พร้อมสำหรับ Edge (รันโค้ดใกล้ผู้ใช้)

เราจะใช้คำสั่ง INCR ของ Redis® เพื่อเพิ่ม quota ของ user ทีละ 1 คำสั่งนี้เป็น atomic หมายถึง execute แค่ครั้งเดียว และเร็วมาก

นี่คือโค้ดเดียวกับก่อนหน้า แต่ใช้ Upstash Redis® จัดการ quota ของ user:

// pages/api/spreadsheets/[id]/index.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { Redis } from "@upstash/redis";

const MAX_FREE_TIER_QUOTA = 1000;

// Generate Upstash Redis instance จาก environment variables
// ตรวจสอบว่า defined ใน .env file แล้ว
const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL!,
  token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  // ดึง ID ของ spreadsheet จาก URL
  const { id } = req.params;
  
  // ดึง spreadsheet และ user ที่เกี่ยวข้องจาก database
  const spreadsheet = await prisma.spreadsheet.findUnique({
    where: { id: Number(id) },
    include: { user: true },
  });
  
  // Verify spreadsheet exists
  if (!spreadsheet) {
    return res.status(404).json({ error: "Spreadsheet not found" });
  }
  
  // Redis quota key unique สำหรับแต่ละ user โดย ID
  const quotaKey = `user:${spreadsheet.user.id}:quota`;
  
  // ดึง quota ของ user จาก Redis
  const quota = await redis.incr(quotaKey);
  
  // ถ้า key ไม่มีอยู่ก่อน ค่าที่คืนจะเป็น 1
  // ตั้ง expiration 1 วัน
  if (quota === 1) {
    await redis.expire(quotaKey, 60 * 60 * 24);
  }
  
  // Verify user not over quota
  if (quota > MAX_FREE_TIER_QUOTA) {
    return res.status(429).json({ error: "Quota exceeded" });
  }
  
  // ส่งคืนเนื้อหา spreadsheet
  return res.json({ content: spreadsheet.content });
}

การใช้ Upstash Redis® ทำให้โค้ด scalable ได้ง่าย ใช้พลังของ Upstash Redis® Edge รันโค้ดใกล้ผู้ใช้

สรุป

Upstash Redis® ทำให้ implement caching system สำหรับ serverless environment ง่ายมาก

เราใช้ implement quota system แต่วิดีโอแนวคิดเดิมนำไปใช้ได้กับ use-cases มากกว่านี้ เช่น caching database queries เพิ่มเติม หรือ caching ผลลัพธ์ของ API request

เป้าหมายสุดท้ายของ caching คือทำให้ API เร็วขึ้น และน่าจะ consume ทรัพยากรน้อยลง (เช่น Planetscale database read/writes queries)

ในบทความหน้า เราจะดูวิธีใช้ Upstash QStash ดึง quota ของ user จาก Redis® เก็บใน database เพื่อให้ quota ของ user ทันสมัยเสมอและไม่หาย

ดู Live

อยากดู optimization แบบนี้ live ไปเช็ก fastsheet API service ที่แปลง Google Sheets เป็น API ในไม่กี่คลิก ฟรีใช้งานพร้อม free tier ใหญ่