Trong bài viết này, chúng ta sẽ xây dựng một ứng dụng TODO đơn giản sử dụng SvelteKit và Redis Serverless (Upstash).
SvelteKit là một framework để xây dựng ứng dụng web mọi quy mô, mang lại trải nghiệm phát triển tuyệt vời và hệ thống định tuyến dựa trên hệ thống tệp linh hoạt.
Tạo dự án
Chạy lệnh sau:
npm init svelte@next todo-app-with-redis

Dự án đã sẵn sàng. Bây giờ cài đặt các phụ thuộc và chạy:
npm install
npm run dev

Giao diện người dùng
Chúng ta tạo một form đơn giản và một danh sách để hiển thị các mục TODO. Hiện tại nó sẽ trống, sau này chúng ta sẽ kết nối với cơ sở dữ liệu.
// 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: 'Tạo cơ sở dữ liệu', status: true },
{ id: '2', text: 'Sao chép URL cơ sở dữ liệu', 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="Thêm todo" class="input" placeholder="Cần làm gì?" />
</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>
Đây là component TODO của chúng ta:
// 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="Đánh dấu todo là {todo.status ? 'chưa hoàn thành' : 'hoàn thành'}">
{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="Xóa todo">✕</button>
</form>
</div>
Bây giờ bạn sẽ thấy:

Chuẩn bị cơ sở dữ liệu
Chúng ta sẽ lưu trữ dữ liệu trong Upstash Redis. Vì vậy, hãy tạo một cơ sở dữ liệu Upstash. Chúng ta sẽ sử dụng client Upstash dựa trên HTTP, rất phù hợp với môi trường Serverless. Cài đặt:
npm install @upstash/redis
Bây giờ tạo cơ sở dữ liệu Redis trên Upstash Console. Sao chép/dán UPSTASH_REDIS_REST_URL và UPSTASH_REDIS_REST_TOKEN vào file .env.
UPSTASH_REDIS_REST_URL=https://global-renewing-gecko-31543.upstash.io
UPSTASH_REDIS_REST_TOKEN=AXs3ACsjfg684jJBFQgN34je7RFJ58wYjg4NjMt=
Chúng ta cần cài đặt dotenv để sử dụng biến môi trường.
npm install dotenv
Tạo file lib/redis.ts và khởi tạo instance Redis như sau:
// 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;
Tạo API
Trong thư mục, file .js/.ts được dùng làm API backend. Vậy index.svelte là frontend, index.ts là backend.
Tạo index.ts và thêm các phương thức API cần thiết.
// src/routes/index.ts
import redis, { databaseName } from "../lib/redis";
import type { RequestHandler } from "@sveltejs/kit";
const redirect = {
status: 303,
headers: { location: "/" },
};
export const get: RequestHandler = async () => {
// chạy mỗi khi trang được tải
return { body: { todos: [] } };
};
export const post: RequestHandler = async ({ request }) => {
// form: tạo todo
return redirect;
};
export const patch: RequestHandler = async ({ request }) => {
// form: cập nhật todo
return redirect;
};
export const del: RequestHandler = async ({ request }) => {
// form: xóa todo
return redirect;
};
POST Tạo Todo
Triển khai tạo mục TODO:
// src/routes/index.ts
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;
};
GET Lấy danh sách Todos
Bây giờ liệt kê các mục TODO:
// src/routes/index.ts
export const get: RequestHandler = async () => {
let todos = [];
const data = await redis.hgetall(databaseName);
if (!data) return { body: { todos } };
// chuẩn hóa dữ liệu
todos = Object.keys(data)
.map((key) => ({
id: key,
text: data[key]["text"],
status: data[key]["status"],
}))
// id = timestamp
.sort((a, b) => parseInt(b.id) - parseInt(a.id));
return { body: { todos } };
};
Để giữ bài viết đơn giản, tôi không thêm code
xóa tác vụvàhoàn thành tác vụở đây, nhưng bạn có thể xem chúng trong repo GitHub.
Mọi thứ đã sẵn sàng!
Mã nguồn | Demo App
Tôi dự định triển khai cùng ứng dụng TODO này với Next.js. Sau đó tôi sẽ so sánh trải nghiệm của mình với các framework này.
Hãy theo dõi chúng tôi trên Twitter và Discord.