Skip to content
youhoc
  • Pages
    • Home
    • Modern App Guidelines
    • Node.js
      • Installing & Exploring
      • Loading Modules
      • npm - Get Command Input
      • Express.js
        • Express Web Server
        • Template Engine & MVC
        • Authentication
        • Authentication Trong REST API Với JWT
        • File Upload with Multer, Express.js
        • Server-Side Validation Với Express-Validator
      • Sequelize
        • Sequelize Transactions: Đảm Bảo Tính Toàn Vẹn Dữ Liệu
        • 7 loại Data Types phổ biến Trong Sequelize
        • Phân Trang (Pagination) Trong Express.js Với Sequelize/MySQL
      • Hướng dẫn Cơ bản về Rest API
      • Node-cron Simple to Complex Setup with PM2
      • Hono
        • Hono Response
        • Error Handling
    • Cloudflare
      • icon picker
        Minimal Cloudflare Worker + Hono + Drizzle ORM (part 1)
      • Minimal Cloudflare Worker + Hono + Drizzle ORM (part 2)
    • htmx
      • HTMx Form: Request, Response, Swap
    • Linux
      • Day 1: Linux Distributions & Navigation
      • Day 2: User Management
      • Day 3: File Permission & Ownership
      • Day 4: Package Management
      • Day 5: Services Management
    • Javascript
      • JS The Weird Part
        • Execution Context
        • Types & Operators
        • Objects & Functions
        • Error Handling & Strict Mode
        • Typescript, ES6, Tra
      • Modern JS
        • JS in the Browser
        • Data Storage JSON
        • Modern JS
        • Advanced Objects & Methods
        • Webpack & Babel
        • Async
      • jQuery
        • In-depth Analysis of jQuery
      • React-ready JS
        • Arrow Function
        • Template Literals
        • Logical AND, OR, Ternary, Nullish Operators
        • Destructuring & Rest Operator
        • Array Method
        • Immutability and Spread Operator
        • Promises, Async/Await, Callback
    • Typescript
      • TypeScript cơ bản (phần 1)
      • TypeScript cơ bản (phần 2)
      • require vs import
    • ReactJS
      • React from Andrew
        • Summary from Next
        • 1. Basics
        • 2. React Components
        • 3. Webpack
        • 4. Styling with SCSS
        • 5. React Router
        • 6. React Hook
      • Modern React From The Beginning
        • Intro to JSX
        • Vite Build Tools
        • Basic Component Creation
        • Component State
        • Props & Component Composition
        • useState with Inputs & Form Submission
        • useEffect, useRef & Local Storage
        • Async / Await and Http Request in React
        • React Router: Declarative Mode
        • ContextAPI
        • React Router: Framework Mode
          • File-routing & HTML Layouts
          • Server-side Data Query
          • Links & Navigation
          • Loaders
    • PHP
      • gruntJS
      • composer
      • MySQL
      • Thiết lập Cloudflare Turnstile chống spam trong PHP
    • Docker
      • Container Basics
      • Container Networking
      • Container Image
      • Container Volume & Persistent Data
      • Dockerfile
      • Docker Compose
      • Docker Registry
    • Payload CMS

Minimal Cloudflare Worker + Hono + Drizzle ORM (part 1)

Một dự án Cloudflare Worker Serverless tối giản, tương đương với Node.js, Express.js và Sequelize.
Khi bắt đầu với Cloudflare Workers, chúng ta thường rơi vào một tình huống khá quen thuộc: muốn dựng thật nhanh một worker nhỏ để thử routing, trả về response đơn giản...
Nhưng chỉ cần nghĩ đến số lượng file trong một template mặc định, một mớ cấu hình, cấu trúc file... là đã thấy hơi rối.
Trong những lúc như vậy, một project tối giản lại rất có giá trị. Chỉ cần đủ ít để nhìn ra luồng chạy của ứng dụng, nhưng cũng đủ rõ để sau này mở rộng tiếp.
Dự án này của mình một ví dụ như vậy.
Một dự án Cloudflare Worker Serverless tối giản, tương đương với Node.js, Express.js và Sequelize.
Những gì đã học: Javascript, Node.js, Express.js, Sequelize
Những gì sẽ áp dụng: Typescrypt, Node.js Compat trên Cloudflare Worker, Hono, Drizzle ORM
Trong Phần 1, chúng ta sẽ review initial commit đầu tiên của mình (đã chạy được), để rút ra câu trả lời cho câu hỏi quan trọng: cần những gì để spin up một Hono Worker thật đơn giản?

Initial Commit này đang có gì?

Commit đầu tiên tạo ra một bộ khung rất gọn, có thể hình dung như sau:
Trong đó, có 3 file quan trọng cần thiết để chạy worker đầu tiên:
package.json để khai báo dependency và script chạy app
wrangler.toml để Cloudflare biết entry point của Worker
src/index.ts làm nơi khởi tạo Hono app
Điểm hay của commit này là không cố làm nhiều việc cùng lúc. Nó chỉ giải quyết 2 endpoint rất cơ bản:
/ trả về hello world
/sitemap.xml trả về XML sitemap
Vậy là đã đủ để thấy toàn bộ luồng của một Worker:
request đi vào src/index.ts
route được mount từ siteRoutes
controller nhận Context của Hono
controller trả về response text hoặc XML
với route sitemap, dữ liệu được lấy từ model

Hono Worker là gì, và vì sao cấu trúc này dễ học?

Trước khi đi vào code, mình chốt nhanh 3 khái niệm:
Cloudflare Worker là môi trường chạy JavaScript/TypeScript ở edge, phù hợp cho các endpoint nhỏ, nhanh, không cần dựng nguyên một server truyền thống.
Hono là web framework rất gọn cho Workers và nhiều runtime khác. Nó lo phần routing, request context, response helper...
Wrangler là CLI và công cụ cấu hình để chạy local, deploy, và kết nối project với Cloudflare Workers.

Cần tối thiểu những gì để spin up một Hono Worker?

Nếu chỉ muốn dựng một Hono Worker thật cơ bản, KHÔNG SỬ DỤNG TEMPLATE MẶC ĐỊNH CỦA CLOUDFLARE VỚI MỘT MỚ CODE TYPESCRIPT HỖN ĐỘN, bạn chỉ cần 4 phần.

1. Dependency

Trong package.json, initial commit chỉ cần:
Ý nghĩa rất đơn giản:
hono để viết app
wrangler để chạy local và deploy
dev để bật môi trường local
deploy để đẩy Worker lên Cloudflare
Với một Worker cực nhỏ, chừng này là đủ.

2. File cấu hình Wrangler

wrangler.toml trong initial commit cũng rất ngắn:
Ở đây cần chú ý 3 dòng:
name: tên Worker
main: file entry point
compatibility_date: mốc tương thích runtime của Cloudflare
Nếu thiếu main, Wrangler sẽ không biết bắt đầu từ đâu. Đây là một trong những cấu hình tối thiểu bắt buộc.

3. Entry point khởi tạo Hono app

Phần quan trọng nhất nằm ở src/index.ts:
Đây chính là bộ khung nhỏ nhất của app:
import Hono
tạo app
mount route
export app mặc định để Worker runtime sử dụng
Nếu bạn muốn giản lược hơn nữa, thậm chí có thể viết tất cả trong một file:
Chỉ vậy là đã có một Hono Worker chạy được.
Và lúc này mặc dù đuôi file .ts vẫn chưa có types gì đáng chú ý.

4. Áp dụng mô hình code MVC

Bản Initial Commit tách route và controller ra 2 file riêng.
Sở dĩ mình làm vậy vì đã quen với mô hình MVC từ Express.js. Sau khi cài Drizzle ORM thì mình có thay đổi tên thư mục theo chuẩn của Drizzle.
Với project học tập hoặc proof of concept, đây là mức tách file vừa đủ đẹp.

4.1. Endpoint hello world: ví dụ nhỏ nhất nhưng rất cần thiết

Trong src/controllers/siteController.ts, route / được xử lý như sau:
Đây là ví dụ rất tốt cho người mới vì nó cho thấy 2 điều:
Hono truyền vào một Context
c.text() là cách nhanh nhất để trả về plain text response
Khi mới spin up Worker, bạn gần như luôn nên có một endpoint kiểu này trước. Nó giúp kiểm tra:
app đã boot thành công chưa
route có chạy không
Wrangler local có map request đúng không
Nói ngắn gọn: trước khi làm auth, database hay middleware, hãy để / trả về được một dòng text rõ ràng.

4.2 Endpoint thực tế hơn: trả về sitemap.xml

Điểm thú vị của initial commit là không dừng ở hello world, mà mình thêm luôn một bài toán thực tế nhỏ: sinh XML sitemap từ dữ liệu mẫu.
Cũng trong src/controllers/siteController.ts, route / được xử lý như sau:
Ở đây có vài điểm rất đáng học:
dữ liệu không hard-code trong controller mà đi qua model
XML được build bằng template string, đủ đơn giản để đọc
response trả về Content-Type: application/xml
Phần này cho thấy một Hono Worker không chỉ dùng để trả JSON API. Nó hoàn toàn phù hợp cho những endpoint nhỏ như:
sitemap
robots.txt
health check
webhook receiver
redirect handler
Đó là những use case rất hợp với Workers: nhỏ, rõ, chạy nhanh.

5. Model trong commit đầu tiên đơn giản đến mức nào?

src/models/sitemapModel.ts chỉ làm đúng một việc: đọc JSON và trả về dữ liệu đã có type.
Mô hình này hợp lý trong giai đoạn đầu vì:
không cần database ngay
không phát sinh async hoặc query phức tạp
người mới vẫn nhìn rõ ranh giới giữa controller và data layer
Nó cũng phản ánh đúng tinh thần của initial commit: làm thứ nhỏ nhất có thể, nhưng vẫn giữ cấu trúc đủ sạch để mở rộng về sau.
Trong commit thứ 2, mình sẽ thêm Drizzle ORM cùng với các thao tác tương tự Sequelize: tạo mode, schema, method...



Mở rộng: thêm custom domain bằng code

Nếu bạn muốn gắn thêm custom domain cho Worker của mình, thì không cần cấu hình custom domain bằng thao tác tay rồi để đó, mà đưa thẳng cấu hình vào wrangler.toml:
Điều kiện là domain này của bạn phải nằm trong danh sách DNS trên Cloudflare, như vậy Cloudflare mới tự động cấu hình DNS phù hợp giúp bạn bằng code.
Ý nghĩa của phần này là:
[[routes]] khai báo một route mapping cho Worker
pattern là domain hoặc pattern mà Worker sẽ phục vụ
custom_domain = true cho biết đây là custom domain, không phải route wildcard kiểu truyền thống
Đây là một bước mở rộng rất thường gặp.

Nếu muốn tự dựng lại từ đầu, các bước ngắn gọn là gì?

Bạn có thể tóm tắt quy trình thành 5 bước:

Bước 1. Cài package

Bước 2. Tạo wrangler.toml

Bước 3. Tạo src/index.ts

Bước 4. Thêm script chạy local

Bước 5. Chạy app hoặc deply

Sau đó mở endpoint local mà Wrangler in ra, bạn sẽ thấy hello world.
Đó là phiên bản tối giản nhất của một Hono Worker.

Kết luận

Từ review commit Initial commit, mình nghĩ có thể rút ra một kết luận khá rõ: để spin up một Hono Worker thật đơn giản, bạn không cần quá nhiều thứ. Chỉ cần hono, wrangler, một file wrangler.toml, một src/index.ts, và một route đủ nhỏ để kiểm tra app đang chạy.
Phần còn lại là tổ chức code sao cho dễ hiểu. youhono chọn cách khá thân thiện với người từng dùng Express: tách routes, controllers, và models từ rất sớm, nhưng vẫn giữ project ở mức tối giản. Đây là một điểm khởi đầu đẹp nếu bạn muốn học Hono mà không bị ngợp.
Nếu đi tiếp từ đây, những bước tự nhiên kế tiếp sẽ là:
thêm type cho environment bindings
chuyển dữ liệu tĩnh sang D1 hoặc KV
thêm auth hoặc middleware
chuẩn hóa cấu trúc cho nhiều route hơn
Nhưng trước tất cả những thứ đó, hãy chắc rằng bạn đã có một Worker nhỏ chạy ổn, hiểu được luồng request từ route đến response, và biết vì sao cấu trúc tối giản lại quan trọng. Initial Commit này làm khá tốt đúng điều đó.
Want to print your doc?
This is not the way.
Try clicking the ··· in the right corner or using a keyboard shortcut (
CtrlP
) instead.