บทนำ
หากคุณเคยต้องสร้างแอปที่ติดตามการใช้งาน จำกัดการใช้ทรัพยากร หรือดึงข้อมูลจากแคชเพื่อเพิ่มประสิทธิภาพ คุณจะรู้ว่า Redis เป็นคำตอบสำหรับความต้องการเหล่านี้! Redis เป็นฐานข้อมูล In-Memory แบบ Key-Value เป็น Open Source และย่อมาจาก Remote Dictionary Server
ในบทความนี้ เราจะพูดถึง Upstash ฐานข้อมูล Redis และการปล่อย Beta ของ Vue SSR Framework ล่าสุดคือ Nuxt 3 นี่คือบทความสำหรับผู้เริ่มต้นที่สำรวจฐานข้อมูล Redis โดยเราจะสร้างแอปพื้นฐานที่ติดตามการเข้าชมหน้าเว็บของแอป Nuxt
ทรัพยากร
- GitHub Repo: https://github.com/Krutie/upstash-nuxt-demo
- Demo:
- Cloudflare Worker: https://upstash-demo.krutie-patel.workers.dev/contact
- Netlify: https://thirsty-visvesvaraya-a09ab9.netlify.app/
Upstash คืออะไร?
Upstash เป็นบริการที่ให้การเข้าถึงฐานข้อมูล Redis แบบ Serverless นั่นคือเหตุผลที่เราต้องเรียนรู้พื้นฐาน Redis รวมถึง Use Cases และคำสั่งที่มีอยู่เพื่อจัดการข้อมูลประเภทต่างๆ
Redis คืออะไร?
Redis มี Use Cases ยอดนิยมดังนี้:
- Caching ข้อมูลและ Session
- Leaderboards - จัดอันดับชื่อและคะแนนในเกม หรือซอฟต์แวร์ที่สร้างด้วยหลักการ Gamification
- Queues - จัดตารางงานเพื่อประมวลผลภายหลังใน Background
- Usage Metering/Counting - จำกัดการใช้ทรัพยากร ควบคุมการแจกจ่าย หรือเพียงแค่นับในระยะใหญ่เพื่อติดตามและวิเคราะห์การใช้งาน เช่น เว็บอีคอมเมิร์ซ โซเชียลมีเดีย แอปมือถือ ฯลฯ
- Content Filtering - เช่น กรองเนื้อหาต่อต้านรายการคำห้าม
ในระดับพื้นฐาน Redis เก็บข้อมูลเป็น Key-Value Pairs แต่ยังสามารถเก็บข้อมูลในโครงสร้างข้อมูลขั้นสูงได้ เช่น Lists, Sets, Sorted Sets ฯลฯ Redis ยังมีชุดคำสั่งสำหรับจัดการโครงสร้างเหล่านี้ เนื่องจากเราจะใช้หนึ่งในนั้นในแอปตัวอย่าง จึงคุ้มค่าที่จะดูความหมายโดยสรุป
- Lists - คล้าย Array ธรรมดา อนุญาตให้ Push/Pop รายการจากทั้งสองข้าง ดึงรายการเดี่ยว และทำ Operations อื่นๆ คำสั่งขึ้นต้นด้วย L เช่น LPOP, LPUSH, LSET
- Hash - เก็บกลุ่ม Key-Value Pairs ใน Key เดียวของ Redis คำสั่งขึ้นต้นด้วย H เช่น HSET, HGET, HDEL
- Sets - คล้าย Lists แต่ Sets เป็น Unique และเก็บรายการแบบ Unordered ไม่สามารถเรียงลำดับได้ แต่เพิ่ม ลบ และตรวจสอบสมาชิกได้เร็ว คำสั่งขึ้นต้นด้วย S เช่น SADD, SCARD, SISMEMBER
- Sorted Sets - คล้าย Sets แต่อนุญาตให้เรียงลำดับด้วย Scores คล้าย Key-Value Pair สามารถจัดการและเรียงลำดับคะแนนตัวเลขได้ คำสั่งขึ้นต้นด้วย Z เช่น ZADD, ZINCRBY, ZSCORE
เรียนรู้คำสั่ง Redis อื่นๆ ได้ที่ https://redis.io/commands
การตั้งค่า Upstash
ทำตามคำแนะนำเพื่อสร้างบัญชีและฐานข้อมูลตามเอกสารที่ https://docs.upstash.com/
ก่อนสร้าง Nuxt App ให้แน่ใจว่าบัญชี Upstash พร้อมแล้ว คุณควรสร้างฐานข้อมูลหนึ่งฐานใน Free Tier ได้
เมื่อสร้างฐานข้อมูลเสร็จ คุณสามารถสร้างและเข้าถึง Redis Database ผ่าน Redis Client ใดๆ หรือใช้ Browser-based CLI ใน Upstash Console เพื่อเริ่มต้นทันที
Redis CLI
คุณสามารถตั้งค่า redis-cli บนเทอร์มินัลเพื่อสร้างและเข้าถึง Redis Database โดยตรงจาก Command Line
Redis NPM Packages
เรามี NPM Packages หลายตัวสำหรับโต้ตอบกับ Redis เราจะใช้ 1) @upstash/redis และ 2) ioredis ในโปรเจกต์ Nuxt นี้
มาเรียนรู้ Nuxt 3 กัน ⚡
Nuxt 3 นำเสนอ CLI ใหม่ชื่อ nuxi สำหรับสร้าง Nuxt App
npx nuxi init nuxt3-app
เราจะสร้างไดเรกทอรี /pages และเพิ่ม Route ง่ายๆ ดังนี้:
-pages
--index.vue
--about.vue
--app.vue
app.vue เป็นการแนะนำใหม่ใน Nuxt 3 ทำหน้าที่เป็น Component หลัก จะถูกโหลดสำหรับทุก Route ใน /pages
Nitro Server Engine ⚙️
Nuxt 3 ยังมี Server Engine ใหม่ชื่อ Nitro เราสามารถใช้ Nitro สร้าง Server API Endpoints และ Server Middleware โดยสร้างไดเรกทอรี server พร้อม Sub-directories api และ middleware ทั้ง API และ Middleware ต้อง Export Default Function ที่จัดการ Request และ Return Promise/JSON Data ไม่เหมือน Nuxt 2 เราไม่ต้องกำหนด Server Middleware ใน nuxt.config.js แล้ว
ขั้นตอนสร้างแอปโดยสรุป:
- เชื่อมต่อกับฐานข้อมูล Redis
- สกัดทุก Request ที่ส่งไปยัง Server เพิ่ม Counter ของหน้านั้นทีละ 1 และเก็บค่าใน Redis
- เรียก API จาก Client-side เพื่อดึง Visit Count จาก Redis และแสดงบนหน้า Nuxt
ใช้ REST Redis Client
Upstash มี Redis Client แบบ HTTP/REST คือ @upstash/redis ที่เพิ่มเป็น Dependency ในโปรเจกต์ Nuxt ได้
yarn add @upstash/redis
ยืนยันตัวตน Redis DB
ต้องระบุ Environment Variables จาก Upstash Console (ในส่วน Database):
- REST URL (
UPSTASH_REDIS_REST_URL) - Token (
UPSTASH_REDIS_REST_TOKEN)
Private Runtime Config
Nuxt มี Runtime Configuration ใน nuxt.config.js เพื่อ Expose Environment Variables ฝั่ง Server
// nuxt.config.js
export default defineNuxtConfig({
publicRuntimeConfig: {},
privateRuntimeConfig: {
UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL,
UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN,
},
});
เข้าถึง Environment Variables ได้โดย Import #config
import { Redis } from "@upstash/redis";
import config from "#config";
const redis = new Redis({
url: config.UPSTASH_REDIS_REST_URL,
token: config.UPSTASH_REDIS_REST_TOKEN,
});
วิธี Zero-Config คือเพิ่ม UPSTASH_REDIS_REST_URL และ UPSTASH_REDIS_REST_TOKEN ในไฟล์ .env และสร้าง Redis Instance ด้วย const redis = Redis.fromEnv() โดยไม่ต้องส่งตัวแปรเหล่านี้ให้ Redis (ใช้ได้เฉพาะกับ @upstash/redis)
สกัด Request ด้วย Nuxt Server Middleware
เราจะใช้ Sorted Set เนื่องจาก Unique และอนุญาตให้เรียงลำดับและจัดการ Score ได้ ใช้ zincrby เพื่อเพิ่ม Page Score ทุกครั้งที่มี Request ไปยังหน้านั้น
// server/middleware/pageCount.js
import { Redis } from "@upstash/redis";
import { getRedisKey } from "../utils";
const redis = Redis.fromEnv();
export default async function (req, res, next) {
const redisKey = getRedisKey(req.url);
await redis.zincrby("myPageCounts", 1, redisKey);
next();
}
สร้าง Namespace (Generating Namespace)
Redis เป็น NoSQL ที่เก็บข้อมูลเป็น Key-Value ไม่มี Auto-increment Keys หรือ UUID แบบไดนามิก นี่คือที่มาของ Utility Function getRedisKey()
Function นี้ประมวลผล Request URL และสร้าง Unique Key สำหรับเก็บ Visit Count ต่อหน้า เพื่อป้องกันการเพิ่ม Counter ซ้ำกับ Key เดียวกัน
export const getRedisKey = (url: string) => {
const reqURL = url?.replace("/", ".");
const redisKey = reqURL === "." ? "page.home" : `page${reqURL}`;
return redisKey;
};
เช่น /about จะกลายเป็น page.about
เข้าถึง Redis DB ผ่าน REST API Endpoints
สร้าง API Endpoint ที่ดึง Count/Score ปัจจุบันสำหรับแต่ละหน้า ใน Nuxt 3 มี 2 วิธีดึงข้อมูล: useAsyncData และ useFetch ใน app.vue เราใช้ useAsyncData กับ $fetch จาก ohmyfetch
// app.vue
ส่ง Router Path เป็น Query Parameter เพื่อระบุหน้าที่เข้าถึง สร้าง API Endpoint ที่ server/api/count.ts ใช้ useQuery จาก h3 Library
// server/api/count.ts
import { useQuery } from "h3";
export default async (req, res) => {
let query = await useQuery(req);
const redisKey = getRedisKey(query.path);
};
ใช้ getRedisKey() เดียวกันเพื่อให้ Key ตรงกับที่ Middleware ใช้ เพื่อนำ Key ไปใช้กับ zscore เพื่อดึง Score/Count
// server/api/count.ts
import { Redis } from "@upstash/redis";
const redis = Redis.fromEnv();
export default async () => {
// ...
const count = await redis.zscore("myPageCounts", redisKey);
return { count };
};
ใช้ Redis API โดยตรงด้วย ioredis
สามารถทำได้เหมือนกันด้วย Library ioredis
yarn add ioredis
ขณะใช้ ioredis จะไม่มี Method auth แต่มี Connection String จาก Upstash Console สำหรับเชื่อมต่อ Redis ตั้งค่า UPSTASH_REDIS_CONN เป็น Runtime Config เช่นเดียวกัน
// nuxt.config.js
export default defineNuxtConfig({
publicRuntimeConfig: {},
privateRuntimeConfig: {
UPSTASH_REDIS_CONN: process.env.UPSTASH_REDIS_CONN,
},
});
ใน Middleware สร้าง Instance new Redis() เพื่อเชื่อมต่อและเข้าถึง Redis Commands
// server/middleware/pageCount.js
import config from "#config";
import Redis from "ioredis";
const client = new Redis(config.UPSTASH_REDIS_CONN);
สกัด Request ด้วย Nuxt Server Middleware (ioredis)
// server/middleware/pageCount.js
// ...
export default async function (req, res, next) {
// ...
await client.zincrby("myPageCounts", 1, redisKey);
next();
}
เข้าถึง Redis DB ผ่าน REST API Endpoints (ioredis)
// server/api/count.ts
import config from "#config";
import Redis from "ioredis";
const client = new Redis(config.UPSTASH_REDIS_CONN);
export default async (req: IncomingMessage, res: ServerResponse) => {
// ...
const count = await client.zscore("myPageCounts", redisKey);
return { count };
};
ทดสอบด้วย Upstash CLI
เนื่องจากเก็บข้อมูลใน Sorted Set ใช้ zrange เพื่อดึงรายการทั้งหมด
zrange myPageCounts 0 -1
myPageCounts= ชื่อ Sorted Set0 -1= ช่วง (0 = เริ่มต้น, -1 = รายการสุดท้าย)
คำสั่งข้างต้นแสดง Keys โดยไม่มี Scores ใช้ WITHSCORES เพื่อแสดง Scores:
// SCORE ต่ำสุดก่อน
zrange myPageCounts 0 -1 WITHSCORES
// SCORE สูงสุดก่อน
zrevrange myPageCounts 0 -1 WITHSCORES
// ดึง SCORE สำหรับ page.home
zscore myPageCounts page.home
ขณะทดสอบ API และ Middleware ให้ตรวจสอบ Upstash Console สำหรับกิจกรรมทั้งหมดบนเว็บไซต์
ปรับใช้ Nuxt 3 App
Nitro Server Engine มีบทบาทสำคัญในการ Deploy Nuxt 3 App
Netlify
Push GitHub Repo ตามปกติ แต่ต้องระวัง 3 สิ่ง:
- Build Command:
npm run build| Publish Directory:dist - สร้าง Environment Variables (REST URL, Token, Redis Connection) ใน Netlify ก่อน Deploy
- สร้าง
netlify.tomlใน Root ของโปรเจกต์
// netlify.toml
[build]
functions = ".output/server"
ตัวอย่างบน Netlify: https://thirsty-visvesvaraya-a09ab9.netlify.app/
Cloudflare
Deploy บน Cloudflare Worker จาก Terminal ได้เลย Nuxt 3 แนะนำ Miniflare สำหรับทดสอบ Local และ Wrangler สำหรับ Preview/Publish
เพิ่ม wrangler.toml พร้อม account_id และ Environment Variables
// wrangler.toml
[site]
bucket = ".output/public"
entry-point = ".output"
[build]
command = "NITRO_PRESET=cloudflare yarn nuxt build"
upload.format = "service-worker"
ตัวอย่างบน Cloudflare: https://upstash-demo.krutie-patel.workers.dev/contact
สรุป
เราได้ครอบคลุมการประยุกต์ใช้ Redis พื้นฐานที่สุดด้วย Upstash เพื่อดูว่าเราสามารถผสานรวมกับ SSR Framework อย่าง Nuxt (v3) ได้อย่างไร เพื่อ:
- เชื่อมต่อกับฐานข้อมูล Redis
- สร้าง Unique Keys และเขียน Key-Value Pairs ลงฐานข้อมูล
- อ่านข้อมูลจาก Redis Database
- ปรับใช้แอปไปที่ Netlify และ Cloudflare Workers
Upstash ยังมีตัวเลือกเข้ารหัสการจราจรข้อมูล สำเนาข้อมูลในหลาย Availability Zones และ Cache REST API Responses บน Edge Locations ทั่วโลก (หาได้ใน Upstash Console ใต้แท็บ Details)
สำหรับผู้เริ่มต้นที่อยากสำรวจ Redis DB Upstash ทำให้เริ่มต้นได้ง่ายกับ Front-end Technologies ที่เรารู้จักและชอบ หวังว่าบทความนี้จะเป็นจุดเริ่มต้นที่ดีสำหรับการเดินทาง Redis ของคุณ