Use Case หลักของ Redis คือการเก็บและจัดการ User Sessions เพื่อ Maintain State ระหว่าง Requests ใน Web Apps สามารถทำได้หลายวิธี และเครื่องมือ Serverless ใหม่ๆ มี Option ง่ายๆ สำหรับ Deploy
การจัดการ User Session Data สำคัญต่อ Business Apps หลายอย่าง เช่น Personalization Platforms ใช้ Redis เก็บ Interactions และ Preferences เพื่อเสนอ Content/Products แบบ Customized ใน Gaming ใช้ Redis จัดการ User Data ให้ Multiplayer Smooth ด้วย Real-time Tracking Advertising Platforms ใช้ Redis เก็บ Session Data เพื่อ Optimize Ad Delivery และ Personalize Campaigns ต่อไป ตัวอย่างนี้จะเน้น E-commerce Application สำรวจการใช้ Redis จัดการ Shopping Carts อย่างมีประสิทธิภาพ
Project Description - รายละเอียดโปรเจกต์
ใน Blog Post นี้เราจะใช้ Clerk, Next.js และ Upstash Redis สร้าง Session Store สำหรับ Shopping Application Features ที่จะ Implement:
- Users Sign-up, Sign-in, Sign-out
- แต่ละ User Add/Remove Items ใน Shopping Cart เฉพาะตัว
- Users Update Item Quantities ใน Cart
Features อื่นๆ เกี่ยวกับ QStash และ Upstash Ratelimit:
- Actions บางอย่าง Trigger Events Schedule Emails ผ่าน QStash ส่งโดย Resend เช่น Checkout → Schedule Shipping Confirmation Email 24 ชม. ซื้อ Item → Prompt ให้ Rate หลัง Delay
- Users Rate Items เก็บ Rating Data ใน Data Structures Upstash Redis ใช้ Upstash Ratelimit ควบคุม Flow ป้องกัน Misuse
Demo - สาธิต
Demo ที่ Deploy ไว้ที่นี่
GitHub Repository ที่นี่
Creating the Next.js Application - สร้าง Next.js App
เปิด Terminal สร้าง App:
npx create-next-app@latest
เลือก Options ได้ Template พร้อม:
npx create-next-app@latest
Need to install the following packages: create-next-app@13.4.18
Ok to proceed? (y) y
✔ What is your project named? shopstash
✔ Would you like to use TypeScript? No / -> Yes
✔ Would you like to use ESLint? No / -> Yes
✔ Would you like to use Tailwind CSS? No /> Yes
✔ Would you like to use `src/` directory? -> No / Yes
✔ Would you like to use App Router? (recommended) No / -> Yes
✔ Would you like to customize the default import alias? -> No / Yes
Creating a new Next.js app in /Users/***/shopstash.
Integrating Clerk - ผสาน Clerk
เพิ่ม Clerk ง่ายด้วย Next.js SDK Prebuilt Components/Hooks ติดตั้ง:
npm install clerk@nextjs
สร้าง App บน Clerk Dashboard Config Sign-up Info ตามต้องการ ได้ Credentials ใส่ .env.local:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_********
CLERK_SECRET_KEY=sk_test_********
Config Paths Clerk ใน .env.local:
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/
Wrap Root Layout ด้วย <ClerkProvider> ใช้ Active Session/User Contexts จะ Implement Header Component ต่อไป:
import { ClerkProvider } from "@clerk/nextjs";
import Header from "./components/Header";
export default function RootLayout({ children, }: { children: React.ReactNode; }) {
return (
<ClerkProvider>
<html lang="en">
<body className="bg-white">
<Header />
<main className="container bg-white">
<div className="flex min-h-screen items-start justify-center ">
<div className="mt-5">{children}</div>
</div>
</main>
</body>
</html>
</ClerkProvider>
);
}
ตอนนี้ Clerk ติดตั้งแล้ว ตัดสินใจซ่อน Page ไหนไว้หลัง Authentication ใน middleware.tsx Root Folder:
import { authMiddleware } from "@clerk/nextjs";
export default authMiddleware({});
export const config = {
matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};
App ทั้งหมด Protected เข้าหน้าไหนไม่ Sign In Redirect ไป Index Authenticate
ตอนนี้ต้องการ Sign-up/Sign-in Pages Navigation จาก Header Render ตาม Active User ถ้ามี User → Sign-out + Profile ไม่มี → Sign-in/Sign-up Routes
Header State ไม่มี Active User:

สร้าง Routes สำหรับ User Actions ใช้ Built-in Clerk Components (Customize ได้) Sign-up สร้าง app/sign-in/[[...sign-up]]/page.tsx:
import { SignUp } from "@clerk/nextjs";
const SignUpPage = () => {
return ( <> <SignUp /> </> );
};
export default SignUpPage;
Sign-in คล้ายกัน สร้าง app/sign-in/[[...sign-in]]/page.tsx:
import { SignIn } from "@clerk/nextjs";
const SignInPage = () => {
return ( <> <SignIn /> </> );
};
export default SignInPage;
Clerk Integrated แล้ว พร้อมสร้าง Unique Session Store บน Redis ต่อ User ดึง User ID จาก Clerk เก็บ Session Data บน Upstash Redis ตัวอย่าง Shopping Cart แต่ละ Session เก็บ Cart Items
Concept Cart Item ก่อน Blueprint สร้าง App ต่อ หากต้องการ Items หลากหลาย ChatGPT ช่วยได้ หรือดูจาก GitHub Repo ต้องมี Images เหมาะสมต่อ Item
export const items = [
{ id: 1, title: "Elegant Leather Watch", image: "/images/1.png", description: "A sophisticated leather watch for all occasions.", company: "Timepiece Creations", price: 99.99 },
];
Implementing Shopping Cart - Implement Cart
Cart ควร Accessible จากหลาย Section ให้ Components Render ตาม Cart Contents เช่น Item Detail Page หรือ List Page เห็นว่า Product อยู่ใน Cart แล้วหรือยัง

ใช้ React Context API ให้ Access Cart Operations (Add, Remove, Reset) แบบ Semi-global
Setup Upstash Redis Copy UPSTASH_REDIS_REST_URL และ UPSTASH_REDIS_REST_TOKEN จาก Console ใส่ .env:
UPSTASH_REDIS_REST_URL=<YOUR_URL>
UPSTASH_REDIS_REST_TOKEN=<YOUR_TOKEN>
Cart Context ใน app/context/CartContext.tsx Wrap Context รอบ App หลัก ใช้ Methods ได้Capabilities:
- Add Items และ Adjust Quantities
- Remove Items
- Reset Cart
- Checkout Feature
Algorithm Overview:
- Cart เป็น Session Store เก็บใน Hash บน Upstash Redis Key Unique จาก User ID Format
cart:<USER_ID> - Save Cart Data ใช้ Item IDs เป็น Keys Quantities เป็น Values Redis Commands ทำให้ Modify Cart ง่าย
- Client-side Cart State เป็น Array ของ Item Objects Load ข้อมูลจาก Upstash Redis ใน
useEffectระหว่าง Load Page Changes Trigger Re-render - Redis Data Structures ทำ
addItemremoveItemง่าย ใช้redis.hincrby()จัดการ Add/Remove/Adjust Quantities resetCartลบ Hash Key ด้วยredis.del()
Adding Item to Cart - เพิ่ม Item เข้า Cart
จากมุมมอง Redis Hash Add Item หรือ Change Quantity ใช้ Command เดียวกัน hincrby สร้าง Key Set Value 1 หรือ Increase ตาม increment Parameter
Client-side Mirror Actions เพิ่ม Item ใหม่ หรือ Adjust Quantity ใน Cart State
const addItem = async (id: number) => {
const item = items.find((i) => i.id === id);
if (!item) return;
const doesItemExist = cart.some((i) => { return id === i.id; });
let newCart: Item[];
if (!doesItemExist) {
newCart = [...(cart || []), item];
redis.hincrby(`user:${userId}`, id.toString(), 1);
const newCartItemIDs = { ...cartItems, [id]: 1 };
setCartItemIDs(newCartItemIDs);
setCart(newCart);
} else {
const item = items.find((i) => i.id === id);
const updatedItemQuantities = { ...cartItems, [id]: cartItems[id] + 1 };
setCartItems(updatedItemQuantities);
redis.hincrby(`user:${userId}`, id.toString(), 1);
}
};
Removing Item from the Cart - ลบ Item ออกจาก Cart
Remove คล้าย Add ใช้ hincrby ให้ increment เป็น -1
const removeItem = async (id: number, force: boolean = false) => {
const doesItemExist = cart.some((i) => { return id === i.id; });
if (!doesItemExist) return;
if (cartItems[id] === 1 || force) {
const newCart: Item[] = cart.filter((item: { id: number }) => { return item.id !== id; });
const newCartItems = { ...cartItems };
delete newCartItems[id];
redis.hdel(`user:${userId}`, id.toString());
setCart(newCart);
setCartItems(newCartItems);
} else if (cartItems[id] > 1) {
const updatedItemQuantities = { ...cartItems, [id]: cartItems[id] - 1 };
setCartItems(updatedItemQuantities);
redis.hincrby(`user:${userId}`, id.toString(), -1);
}
};
Cart Functionality พร้อม Methods ทั้งหมด Integrated ผ่าน Project Global Access ตัวอย่าง Use Cases:
- Index Page: แสดง Items ทั้งหมด พร้อม Buttons Add/Remove หากอยู่ใน Cart แล้ว
- ใช้
shadcnuiReact UI Library สร้าง Modal/Sheet รวม Cart Items หน้าเดียว Adjust Quantities, Reset, Checkout
Index Page - หน้าหลัก
Items แสดงเฉพาะ Active Signed-in Users Fetch User Data จาก Clerk Render Components ตาม Response
Card Component ดึง Functions/Objects จาก CartContext:
export default function CardComponent(props: { item: cardProps }) {
const { item } = props;
const { id, title, image, company } = item;
const { addItem, removeItem, cartItems } = useContext(CartContext);
return (
<>
<Card className="transition duration-200 hover:shadow-lg">
<Link href={`/products/${id}`}>
<CardHeader>
<CardTitle>{title}</CardTitle>
</CardHeader>
<CardContent>
<Image src={image} alt={title} width={300} height={300}></Image>
<CardDescription>{company}</CardDescription>
</CardContent>
</Link>
<CardFooter>
<div className="grid grid-rows-2">
<CartButton id={id} cartItems={cartItems} addItem={addItem} removeItem={removeItem} />
</div>
</CardFooter>
</Card>
</>
);
}
Cart Button สำคัญ Render ตาม Cart State เพิ่ม/ลบ Item:
const CartButton = ({ id, cartItems, addItem, removeItem, }: { id: number; cartItems: cartContent; addItem: (id: number) => Promise<void>; removeItem: (id: number, force: boolean) => Promise<void>; }) => {
const itemExists: boolean = cartItems?.hasOwnProperty(id);
const { triggerEvent } = useContext(UserStateContext);
return (
<button className={`${ itemExists ? "bg-red-400 text-black" : "bg-cyan-500 text-black" } flex items-center justify-center gap-3 rounded-full px-4 py-2 transition-all duration-300`} onClick={() => { if (itemExists) { removeItem(id, true); } else { addItem(id); } }} >
<p className="text-sm font-bold"> {itemExists ? "Remove from Cart" : "Add to Cart"} </p>
<FaCartShopping size="25" />
</button>
);
};
Cart State Dynamic Prompt Component Render ตาม Status สร้าง Dedicated Cart Component ดู Items ทั้งหมด Adjust Quantities Reset
ใช้ shadcn/ui Sheet Component Base Customize Interior
Cart ว่าง:

Cart มี Items เห็น Selections Adjust Quantities ดู Total Value:

Full Walkthrough Component Creation ดูด้านล่าง รายละเอียด Button Configs ดู GitHub Repo
โปรเจกต์เสร็จสิ้น สร้าง Cart Feature ด้วย Upstash Redis และ shadcn/ui
Conclusion - สรุป
Clerk จัดการ User + Upstash Redis เก็บข้อมูลประสิทธิภาพ = Backbone ของ Dynamic Cart System ทั้ง Security และ Performance ตัวอย่างเครื่องมือ Upstash Redis, Clerk ร่วมกันแก้ปัญหาซับซ้อนได้ลื่นไหล
แนะนำปรับปรุงเพิ่มเติม:
- User Experience: Animations, Feedback Loops, Detailed Product Previews
- Performance: Advanced Caching Strategies, Service Workers สำหรับ Load Times และ Offline Experience
- Features: Wishlists, Recommendations ตาม Cart Contents, Promo Codes
- Integration: Payment Gateways Checkout Smooth, Third-party Inventory/CRM Systems
ขอบคุณที่ติดตาม 歡迎 Feedback และ Innovations จากคุณ! คำถาม/ปัญหา ติดต่อ fahreddin@upstash.com
GitHub Repository ที่นี่
ส่วน Schedule Emails ด้วย QStash และ Resend จะครอบคลุมใน Post ถัดไป ตอนนี้ดู Example Repo ได้