ในบทความนี้ เราจะสร้างแอปพลิเคชัน TODO แบบง่ายๆ โดยใช้ SvelteKit ร่วมกับ Serverless Redis (Upstash) เพื่อให้ได้แอปที่รวดเร็ว สามารถขยายตัวได้ และเหมาะสำหรับสภาพแวดล้อม Serverless
SvelteKit เป็นเฟรมเวิร์กสำหรับสร้างแอปพลิเคชันเว็บทุกขนาด พร้อมประสบการณ์การพัฒนาที่ยอดเยี่ยม และระบบ Routing แบบ Filesystem ที่ยืดหยุ่น
1. สร้างโปรเจกต์
รันคำสั่งด้านล่างเพื่อสร้างโปรเจกต์ SvelteKit ใหม่:
npm init svelte@next todo-app-with-redis
ตอบคำถามการตั้งค่าตามที่ต้องการ (เลือก TypeScript, ESLint, Prettier เป็นต้น) จากนั้นติดตั้ง Dependencies และรันเซิร์ฟเวอร์พัฒนา:
npm install
npm run dev
เปิดเบราว์เซอร์ที่ https://localhost:5173 คุณจะเห็นหน้าแรกของ SvelteKit
2. สร้าง User Interface
เราจะสร้างฟอร์มเพิ่ม TODO และรายการแสดงผล โดยแบ่งเป็น 2 ส่วน: งานที่ยังไม่เสร็จ และงานที่เสร็จแล้ว
ไฟล์ src/routes/index.svelte
<script lang="ts">
import type { Todo } from "../lib/types";
import TodoItem from "../components/Todo.svelte";
export let todos: Todo[] = [
{ id: '1', text: 'Create database', status: true },
{ id: '2', text: 'Copy database url', status: false }
];
const uncheckedTodos = todos.filter((todo) => !todo.status);
const checkedTodos = todos.filter((todo) => todo.status);
</script>
<main class="container">
<form class="new" action="/" method="post">
<input
type="text"
name="text"
autofocus
aria-label="Add todo"
class="input"
placeholder="What needs to be done?"
/>
</form>
{#if uncheckedTodos.length}
<div class="todos">
{#each uncheckedTodos as todo (todo.id)}
<TodoItem {todo} />
{/each}
</div>
{/if}
{#if checkedTodos.length}
<div class="todos todos-done">
{#each checkedTodos as todo (todo.id)}
<TodoItem {todo} />
{/each}
</div>
{/if}
</main>
คอมโพเนนต์ TODO (src/components/Todo.svelte)
<script lang="ts">
import type { Todo } from "../lib/types";
export let todo: Todo;
</script>
<div class="todo">
<form action="/?_method=PATCH" method="post">
<input type="hidden" name="todo" value={JSON.stringify(todo)} />
<button
class="checkbox"
aria-label="Mark todo as {todo.status ? 'not done' : 'done'}"
>
{todo.status ? "✓" : ""}
</button>
</form>
<span class="text">{todo.text}</span>
<form action="/?_method=DELETE" method="post">
<input type="hidden" name="id" value={todo.id} />
<button class="delete" aria-label="Delete todo">✕</button>
</form>
</div>
3. เตรียมฐานข้อมูล Upstash Redis
เราจะใช้ Upstash Redis ซึ่งเป็น Redis แบบ Serverless ที่เข้าถึงผ่าน HTTP REST API เหมาะสำหรับ Edge/Serverless Functions
- สร้างฐานข้อมูลที่ Upstash Console
- คัดลอก
UPSTASH_REDIS_REST_URLและUPSTASH_REDIS_REST_TOKEN - ติดตั้งแพ็กเกจที่จำเป็น:
npm install @upstash/redis dotenv
สร้างไฟล์ src/lib/redis.ts
// src/lib/redis.ts
import "dotenv/config";
import { Redis } from "@upstash/redis";
export const databaseName = process.env.NODE_ENV === "development"
? "redis-with-svelte-kit-dev"
: "redis-with-svelte-kit";
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL,
token: process.env.UPSTASH_REDIS_REST_TOKEN,
});
export default redis;
สร้างไฟล์ .env ในรูทโปรเจกต์และใส่ค่าที่ได้จาก Upstash Console:
UPSTASH_REDIS_REST_URL=https://your-database.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token-here
4. สร้าง API Endpoints
ใน SvelteKit ไฟล์ +page.svelte เป็น Frontend ส่วน +page.server.ts (หรือ index.ts ในเวอร์ชันเก่า) จะเป็น Backend API
ไฟล์ src/routes/index.ts
// src/routes/index.ts
import redis, { databaseName } from "../lib/redis";
import type { RequestHandler } from "@sveltejs/kit";
const redirect = {
status: 303,
headers: { location: "/" },
};
// GET - โหลดรายการ TODO ทั้งหมด
export const get: RequestHandler = async () => {
let todos: any[] = [];
const data = await redis.hgetall(databaseName);
if (!data) return { body: { todos } };
// Normalize และจัดเรียงตาม ID (timestamp) ใหม่สุดก่อน
todos = Object.keys(data)
.map((key) => ({
id: key,
text: data[key]["text"],
status: data[key]["status"],
}))
.sort((a, b) => parseInt(b.id) - parseInt(a.id));
return { body: { todos } };
};
// POST - สร้าง TODO ใหม่
export const post: RequestHandler = async ({ request }) => {
const form = await request.formData();
const text = form.get("text");
const id = Date.now().toString();
const todo = JSON.stringify({ text, status: false });
await redis.hset(databaseName, id, todo);
return redirect;
};
// PATCH - อัปเดตสถานะ TODO
export const patch: RequestHandler = async ({ request }) => {
const form = await request.formData();
const todoStr = form.get("todo");
const { id, text, status } = JSON.parse(todoStr as string);
await redis.hset(databaseName, { [id]: { text, status: !status } });
return redirect;
};
// DELETE - ลบ TODO
export const del: RequestHandler = async ({ request }) => {
const form = await request.formData();
const id = form.get("id");
await redis.hdel(databaseName, id as string);
return redirect;
};
5. สรุปและทดสอบ
ตอนนี้แอป TODO ของคุณพร้อมใช้งานแล้ว! ลองรัน npm run dev และทดสอบการเพิ่ม แก้ไข ลบ และติ๊กสถานะงาน
โค้ดต้นฉบับ: GitHub Repository
Demo App: Live Demo
ขอบคุณที่อ่านครับ! ติดตามบทความต่อไปที่จะเปรียบเทียบกับ Next.js และ Remix นะครับ