Trong thế giới phát triển web đang thay đổi nhanh chóng, việc tìm kiếm framework phù hợp có thể là một thách thức. Bạn cần một thứ gì đó nhanh, nhẹ và đủ linh hoạt để hoạt động trên các môi trường khác nhau. Hono xuất hiện – một framework web đang nhanh chóng thu hút sự chú ý của các nhà phát triển nhờ tốc độ ấn tượng, dung lượng tối thiểu và thiết kế thân thiện với nhà phát triển.
Hono (có nghĩa là "ngọn lửa" 🔥 trong tiếng Nhật) được đặt tên rất phù hợp – nó cực kỳ nhanh và thắp sáng trải nghiệm phát triển của bạn bằng sự đơn giản, thanh lịch. Cho dù bạn đang xây dựng API, microservice hay ứng dụng full-stack, Hono đều mang đến một lựa chọn hấp dẫn thay vì các framework nặng nề hơn.
Hướng dẫn này sẽ đưa bạn qua mọi thứ bạn cần biết để bắt đầu với Hono.js, từ cài đặt, xây dựng ứng dụng đầu tiên cho đến hiểu các khái niệm cốt lõi của nó.
Bạn muốn một nền tảng Tích hợp, Tất cả trong Một để Nhóm Phát triển của bạn làm việc cùng nhau với năng suất tối đa?
Apidog đáp ứng mọi yêu cầu của bạn và thay thế Postman với mức giá phải chăng hơn nhiều!
Hono là gì?

Hono là một framework web nhỏ, đơn giản và cực nhanh được xây dựng dựa trên Web Standards. Nó hoạt động trên hầu hết mọi runtime JavaScript: Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, Netlify, AWS Lambda, Lambda@Edge và Node.js.
Về cốt lõi, Hono tuân thủ các API Web Standards (như Request, Response và Fetch), điều này mang lại cho nó khả năng di động đáng kể trên các nền tảng. Điều này có nghĩa là bạn có thể viết code một lần và triển khai nó ở hầu hết mọi nơi mà không cần sửa đổi lớn.
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hono!'))
export default app
Ví dụ đơn giản này minh họa cú pháp rõ ràng của Hono, lấy cảm hứng từ Express nhưng được hiện đại hóa cho hệ sinh thái JavaScript ngày nay.
Hãy cùng đi sâu vào!
1. Cài đặt Hono.js với Bun và Thiết lập Dự án
Bun là một runtime JavaScript hiện đại nổi tiếng về tốc độ và bộ công cụ tất cả trong một, bao gồm trình quản lý gói, trình đóng gói và trình chạy thử nghiệm. Thiết lập Hono với Bun rất đơn giản.
Điều kiện tiên quyết
Đảm bảo bạn đã cài đặt Bun. Nếu chưa, bạn có thể cài đặt bằng cách làm theo hướng dẫn trên trang web chính thức của Bun.
Tạo Dự án Hono Mới với Bun
Cách nhanh nhất để bắt đầu một dự án Hono mới với Bun là sử dụng công cụ tạo khung create-hono
:
bun create hono@latest my-hono-app
Lệnh này sẽ nhắc bạn chọn một template. Đối với hướng dẫn này, hãy chọn template bun
.
? Which template do you want to use? › - Use arrow keys. Return to submit.
❯ bun
cloudflare-workers
cloudflare-pages
deno
fastly
netlify
nodejs
vercel
Sau khi chọn template, điều hướng vào thư mục dự án mới của bạn và cài đặt các dependency:
cd my-hono-app
bun install
Thêm Hono vào Dự án Bun Hiện có
Nếu bạn có một dự án Bun hiện có, bạn có thể thêm Hono làm dependency:
bun add hono
2. Xây dựng Ứng dụng Cơ bản với Hono.js: "Hello Hono!"
Hãy tạo một ứng dụng "Hello World" đơn giản (hay đúng hơn là "Hello Hono!").
Bên trong dự án của bạn, bạn sẽ thấy một thư mục src
. Tạo hoặc mở tệp src/index.ts
(hoặc index.js
nếu bạn thích JavaScript thuần túy, mặc dù TypeScript được khuyến nghị để tận dụng hết lợi ích của Hono).
// src/index.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
export default app
Hãy phân tích điều này:
- Chúng ta import lớp
Hono
từ góihono
. - Chúng ta tạo một instance mới của
Hono
, thường được đặt tên làapp
. - Chúng ta định nghĩa một route cho các yêu cầu
GET
đến đường dẫn gốc (/
). - Trình xử lý route là một hàm nhận đối tượng
Context
(thường làc
) làm đối số. c.text('Hello Hono!')
tạo một đối tượng Response với văn bản thuần túy "Hello Hono!".- Cuối cùng, chúng ta export instance
app
. Đối với Bun, export mặc định này là đủ để máy chủ phát triển nhận diện.
Chạy Ứng dụng của Bạn
Để chạy ứng dụng của bạn ở chế độ phát triển, sử dụng script dev
được định nghĩa trong tệp package.json
của bạn (mà create-hono
đã thiết lập sẵn cho bạn):
bun run dev
Thao tác này thường sẽ khởi động một máy chủ tại http://localhost:3000
. Mở URL này trong trình duyệt web của bạn và bạn sẽ thấy "Hello Hono!".
Thay đổi Cổng
Nếu bạn cần chạy ứng dụng của mình trên một cổng khác, bạn có thể sửa đổi phần export trong src/index.ts
:
// src/index.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono on port 8080!')
})
export default {
port: 8080, // Chỉ định cổng mong muốn của bạn
fetch: app.fetch,
}
Bây giờ, khi bạn chạy bun run dev
, ứng dụng sẽ được phục vụ trên http://localhost:8080
.
3. Xây dựng API Cơ bản với Hono.js
Hono rất xuất sắc trong việc xây dựng API. Hãy cùng khám phá định tuyến (routing), cách xử lý yêu cầu và tạo phản hồi.
Định tuyến (Routing)
Định tuyến trong Hono rất trực quan. Instance app
có các phương thức tương ứng với các động từ HTTP (.get()
, .post()
, .put()
, .delete()
, v.v.).
Route GET Cơ bản
Chúng ta đã thấy một route GET cơ bản:
app.get('/hello', (c) => {
return c.text('Hello, world!')
})
Tham số Đường dẫn (Path Parameters)
Bạn có thể định nghĩa các route với tham số đường dẫn sử dụng cú pháp :paramName
. Các tham số này có thể được truy cập thông qua c.req.param('paramName')
.
// Ví dụ: /users/123
app.get('/users/:id', (c) => {
const userId = c.req.param('id')
return c.text(`User ID: ${userId}`)
})
Tham số Truy vấn (Query Parameters)
Các tham số truy vấn từ URL (ví dụ: /search?q=hono
) có thể được truy cập sử dụng c.req.query('queryName')
.
// Ví dụ: /search?category=frameworks&limit=10
app.get('/search', (c) => {
const category = c.req.query('category')
const limit = c.req.query('limit')
return c.text(`Searching in category: ${category}, Limit: ${limit}`)
})
Bạn cũng có thể lấy tất cả các tham số truy vấn dưới dạng một đối tượng với c.req.query()
.
Xử lý các Phương thức HTTP Khác nhau
Hono giúp dễ dàng xử lý các phương thức HTTP khác nhau cho cùng một đường dẫn:
// src/index.ts
import { Hono } from 'hono'
const app = new Hono()
// GET danh sách bài viết
app.get('/posts', (c) => {
// Trong ứng dụng thực tế, bạn sẽ lấy dữ liệu này từ cơ sở dữ liệu
const posts = [
{ id: '1', title: 'Getting Started with Hono' },
{ id: '2', title: 'Advanced Hono Techniques' },
];
return c.json(posts); // Trả về phản hồi JSON
})
// GET một bài viết duy nhất theo ID
app.get('/posts/:id', (c) => {
const id = c.req.param('id')
// Lấy bài viết theo ID từ cơ sở dữ liệu
const post = { id: id, title: `Post ${id}`, content: 'This is the content...' };
if (!post) {
return c.notFound() // Helper tích hợp cho 404
}
return c.json(post)
})
// TẠO bài viết mới
app.post('/posts', async (c) => {
const body = await c.req.json() // Phân tích cú pháp body JSON
// Trong ứng dụng thực tế, bạn sẽ lưu dữ liệu này vào cơ sở dữ liệu
console.log('Creating post:', body)
return c.json({ message: 'Post created successfully!', data: body }, 201) // Phản hồi với mã 201 Created
})
// CẬP NHẬT bài viết hiện có
app.put('/posts/:id', async (c) => {
const id = c.req.param('id')
const body = await c.req.json()
// Cập nhật bài viết trong cơ sở dữ liệu
console.log(`Updating post ${id}:`, body)
return c.json({ message: `Post ${id} updated successfully!`, data: body })
})
// XÓA bài viết
app.delete('/posts/:id', (c) => {
const id = c.req.param('id')
// Xóa bài viết khỏi cơ sở dữ liệu
console.log(`Deleting post ${id}`)
return c.json({ message: `Post ${id} deleted successfully!` })
})
export default {
port: 3000,
fetch: app.fetch
}
Đối tượng Request (c.req
)
Đối tượng Context
(c
) cung cấp quyền truy cập vào chi tiết yêu cầu thông qua c.req
. Các thuộc tính và phương thức chính bao gồm:
c.req.url
: Chuỗi URL đầy đủ.c.req.method
: Phương thức HTTP (ví dụ: 'GET', 'POST').c.req.headers
: Một đối tượng Headers. Truy cập các header nhưc.req.header('Content-Type')
.c.req.param('name')
: Lấy một tham số đường dẫn.c.req.query('name')
: Lấy một tham số truy vấn.c.req.queries('name')
: Lấy tất cả các giá trị cho một tham số truy vấn lặp lại dưới dạng mảng.c.req.json()
: Phân tích cú pháp body yêu cầu dưới dạng JSON. (Trả về một Promise)c.req.text()
: Đọc body yêu cầu dưới dạng văn bản thuần túy. (Trả về một Promise)c.req.arrayBuffer()
: Đọc body yêu cầu dưới dạng ArrayBuffer. (Trả về một Promise)c.req.formData()
: Phân tích cú pháp body yêu cầu dưới dạng FormData. (Trả về một Promise)c.req.valid('type')
: Truy cập dữ liệu đã được xác thực (sử dụng với validator, sẽ được đề cập sau).
Đối tượng Response (c
) và các Helper
Đối tượng Context
(c
) cũng cung cấp các helper tiện lợi để tạo đối tượng Response
:
c.text(text, status?, headers?)
: Trả về phản hồi văn bản thuần túy.c.json(object, status?, headers?)
: Trả về phản hồi JSON.Content-Type
được tự động đặt thànhapplication/json
.c.html(html, status?, headers?)
: Trả về phản hồi HTML.c.redirect(location, status?)
: Trả về phản hồi chuyển hướng (trạng thái mặc định 302).c.notFound()
: Trả về phản hồi 404 Not Found.c.newResponse(body, status?, headers?)
: Tạo một đối tượngResponse
mới.body
có thể lànull
,ReadableStream
,ArrayBuffer
,FormData
,URLSearchParams
hoặcstring
.c.header(name, value)
: Đặt một header phản hồi.
Ví dụ: Đặt Header Tùy chỉnh
app.get('/custom-header', (c) => {
c.header('X-Powered-By', 'HonoJS-Flame')
c.header('Cache-Control', 'no-cache')
return c.text('Check the response headers!')
})
4. Làm việc với Middleware trong Hono.js
Các hàm Middleware là nền tảng của Hono (và nhiều framework web). Chúng là các hàm có thể xử lý yêu cầu trước khi nó đến trình xử lý route chính, hoặc xử lý phản hồi sau khi trình xử lý đã chạy. Middleware rất hữu ích cho các tác vụ như ghi log, xác thực, xác thực dữ liệu, nén, CORS, v.v.
Chữ ký cơ bản của một hàm middleware là async (c, next) => { ... }
.
c
: Đối tượng Context.next
: Một hàm để gọi nhằm chuyển quyền kiểm soát cho middleware tiếp theo trong chuỗi, hoặc cho trình xử lý route cuối cùng. Bạn phảiawait next()
nếu muốn các middleware hoặc trình xử lý tiếp theo thực thi.
Sử dụng Middleware Tích hợp
Hono đi kèm với một bộ middleware tích hợp phong phú. Bạn có thể import chúng từ hono/middleware-name
(ví dụ: hono/logger
, hono/cors
).
Để áp dụng middleware cho tất cả các route, sử dụng app.use(middlewareFunction)
.
Để áp dụng middleware cho các route cụ thể, cung cấp một mẫu đường dẫn làm đối số đầu tiên: app.use('/admin/*', authMiddleware)
.
Ví dụ: Logger và ETag Middleware
// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { etag } from 'hono/etag'
import { prettyJSON } from 'hono/pretty-json' // Để định dạng phản hồi JSON đẹp mắt
const app = new Hono()
// Áp dụng middleware cho tất cả các route
app.use(logger()) // Ghi log thông tin yêu cầu và phản hồi vào console
app.use(etag()) // Thêm header ETag cho caching
app.use(prettyJSON()) // Định dạng phản hồi JSON có thụt lề
app.get('/', (c) => {
return c.text('Hello with Logger and ETag!')
})
app.get('/data', (c) => {
return c.json({ message: 'This is some data.', timestamp: Date.now() })
})
export default app
Khi bạn chạy ứng dụng này và truy cập /
hoặc /data
, bạn sẽ thấy log trong console và các phản hồi sẽ bao gồm header ETag
. Phản hồi JSON từ /data
sẽ được định dạng đẹp mắt.
Các Middleware Tích hợp Hữu ích Khác:
cors
: Xử lý Chia sẻ Tài nguyên Cross-Origin.basicAuth
: Thực hiện Xác thực Cơ bản.jwt
: Xác thực JWT (JSON Web Token).compress
: Nén body phản hồi.cache
: Thực hiện caching sử dụng Cache API.secureHeaders
: Thêm các header HTTP liên quan đến bảo mật khác nhau.bodyLimit
: Giới hạn kích thước của body yêu cầu.
Bạn có thể tìm thấy danh sách đầy đủ và tài liệu trong tài liệu của Hono dưới mục "Middleware".
Tạo Middleware Tùy chỉnh
Bạn có thể dễ dàng viết middleware của riêng mình.
Ví dụ 1: Bộ đếm thời gian Yêu cầu Đơn giản
// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
const app = new Hono()
app.use(logger())
// Middleware tùy chỉnh để đo thời gian xử lý yêu cầu
app.use(async (c, next) => {
const start = Date.now()
console.log(`Request received at: ${new Date(start).toISOString()}`)
await next() // Gọi middleware hoặc trình xử lý route tiếp theo
const ms = Date.now() - start
c.header('X-Response-Time', `${ms}ms`) // Thêm header tùy chỉnh vào phản hồi
console.log(`Request processed in ${ms}ms`)
})
app.get('/', (c) => {
return c.text('Hello from Hono with custom timing middleware!')
})
app.get('/slow', async (c) => {
// Mô phỏng một thao tác chậm
await new Promise(resolve => setTimeout(resolve, 1500))
return c.text('This was a slow response.')
})
export default app
Truy cập /
và /slow
để xem log thời gian và header X-Response-Time
.
Ví dụ 2: Middleware Xác thực API Key Đơn giản
Đây là một ví dụ rất cơ bản để minh họa. Trong một ứng dụng thực tế, hãy sử dụng các phương thức xác thực mạnh mẽ hơn như JWT hoặc OAuth, và lưu trữ API key một cách an toàn.
// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { HTTPException } from 'hono/http-exception' // Để ném các lỗi HTTP chuẩn
const app = new Hono()
app.use(logger())
const API_KEY = "supersecretapikey"; // Trong ứng dụng thực tế, lưu trữ cái này một cách an toàn (ví dụ: biến môi trường)
// Middleware Xác thực API Key Tùy chỉnh
const apiKeyAuth = async (c, next) => {
const apiKeyHeader = c.req.header('X-API-KEY')
if (apiKeyHeader && apiKeyHeader === API_KEY) {
await next()
} else {
// Sử dụng HTTPException để trả về phản hồi lỗi chuẩn hóa
throw new HTTPException(401, { message: 'Unauthorized: Invalid API Key' })
}
}
// Áp dụng middleware này cho một nhóm route cụ thể
app.use('/api/v1/*', apiKeyAuth)
app.get('/api/v1/me', (c) => {
return c.json({ user: 'Authenticated User', email: 'user@example.com' })
})
app.get('/public/info', (c) => {
return c.text('This is public information, no API key needed.')
})
// Trình xử lý lỗi cho HTTPException
app.onError((err, c) => {
if (err instanceof HTTPException) {
return err.getResponse()
}
// Đối với các lỗi khác
console.error('Unhandled error:', err)
return c.text('Internal Server Error', 500)
})
export default app
Để kiểm tra điều này:
curl http://localhost:3000/api/v1/me
(sẽ trả về 401 Unauthorized)curl -H "X-API-KEY: supersecretapikey" http://localhost:3000/api/v1/me
(sẽ trả về dữ liệu người dùng)curl http://localhost:3000/public/info
(sẽ trả về thông tin công khai)
Tái sử dụng Middleware Tùy chỉnh với createMiddleware
Đối với các middleware phức tạp hoặc có thể tái sử dụng hơn, bạn có thể định nghĩa nó riêng biệt bằng cách sử dụng createMiddleware
từ hono/factory
. Điều này cũng giúp với suy luận kiểu TypeScript.
// src/middlewares/timing.ts
import { createMiddleware } from 'hono/factory'
export const timingMiddleware = createMiddleware(async (c, next) => {
const start = Date.now()
await next()
const ms = Date.now() - start
c.res.headers.set('X-Response-Time', `${ms}ms`) // Lưu ý: c.res.headers.set
console.log(` -> Processed in ${ms}ms`)
})
// src/middlewares/auth.ts
import { createMiddleware } from 'hono/factory'
import { HTTPException } from 'hono/http-exception'
const VALID_API_KEY = "anothersecretkey";
// Kiểu cho biến môi trường nếu middleware của bạn cần chúng
type Env = {
Variables: {
user?: { id: string } // Ví dụ: đặt dữ liệu người dùng sau xác thực
}
// Bindings: { MY_KV_NAMESPACE: KVNamespace } // Ví dụ cho Cloudflare Workers
}
export const secureApiKeyAuth = createMiddleware<Env>(async (c, next) => {
const apiKey = c.req.header('Authorization')?.replace('Bearer ', '')
if (apiKey === VALID_API_KEY) {
// Tùy chọn, bạn có thể đặt biến trong context cho các trình xử lý downstream
c.set('user', { id: 'user123' })
await next()
} else {
throw new HTTPException(401, { message: 'Access Denied: Secure API Key Required.'})
}
})
// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { timingMiddleware } from './middlewares/timing' // Giả sử chúng nằm trong thư mục middlewares
import { secureApiKeyAuth } from './middlewares/auth'
const app = new Hono()
app.use(logger())
app.use(timingMiddleware) // Áp dụng cho tất cả các route
app.get('/', (c) => {
return c.text('Hello from Hono with factored middleware!')
})
app.use('/secure/data/*', secureApiKeyAuth) // Chỉ áp dụng cho /secure/data/*
app.get('/secure/data/profile', (c) => {
// const user = c.get('user') // Truy cập biến được đặt bởi middleware
return c.json({ profileData: 'Sensitive profile information', /*user: user*/ })
})
app.get('/public/data', (c) => {
return c.text('This is public data, no key needed.')
})
// Trình xử lý lỗi chung
app.onError((err, c) => {
if (err instanceof HTTPException) {
return err.getResponse();
}
console.error('Error:', err.message);
return c.json({ error: 'Internal Server Error', message: err.message }, 500);
});
export default app
Cấu trúc này làm cho middleware của bạn trở nên module hóa hơn và dễ kiểm thử độc lập hơn.
5. Kiểm thử Ứng dụng Hono.js của Bạn
Hono được thiết kế để dễ dàng kiểm thử. Bun đi kèm với trình chạy thử nghiệm riêng, bun:test
, hoạt động tốt với Hono. Bạn cũng có thể sử dụng các trình chạy thử nghiệm khác như Vitest.
Sử dụng bun:test
(Cơ bản)
Tài liệu bun.md
cung cấp một ví dụ cơ bản về kiểm thử với bun:test
:
Tạo một tệp kiểm thử, ví dụ: src/index.test.ts
:
// src/index.test.ts
import { describe, expect, it } from 'bun:test'
import app from '.' // Import ứng dụng của bạn từ src/index.ts
describe('Hono Application Tests', () => {
it('Should return 200 OK for GET /', async () => {
const req = new Request('http://localhost/') // Base URL không quan trọng lắm ở đây
const res = await app.fetch(req)
expect(res.status).toBe(200)
expect(await res.text()).toBe('Hello Hono!') // Hoặc bất cứ điều gì route gốc của bạn trả về
})
it('Should return JSON for GET /posts', async () => {
const req = new Request('http://localhost/posts')
const res = await app.fetch(req)
expect(res.status).toBe(200)
const json = await res.json()
expect(json).toBeArray() // Giả sử /posts trả về một mảng
// Thêm các assertion cụ thể hơn về nội dung JSON nếu cần
})
it('Should create a post for POST /posts', async () => {
const postData = { title: 'Test Post', content: 'This is a test.' };
const req = new Request('http://localhost/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(postData),
});
const res = await app.fetch(req);
expect(res.status).toBe(201); // Mong đợi 201 Created
const jsonResponse = await res.json();
expect(jsonResponse.message).toBe('Post created successfully!');
expect(jsonResponse.data.title).toBe(postData.title);
});
})
Để chạy các bài kiểm thử của bạn:
bun test
Hoặc, nếu bạn muốn chạy một tệp cụ thể:
bun test src/index.test.ts
Sử dụng Helper app.request()
Hono cung cấp một phương thức app.request()
tiện lợi giúp đơn giản hóa việc kiểm thử bằng cách cho phép bạn trực tiếp truyền đường dẫn và các tùy chọn, thay vì phải xây dựng một đối tượng Request
đầy đủ mỗi lần. Điều này được trình bày trong docs/guides/testing.md
.
// src/index.test.ts
import { describe, expect, it } from 'bun:test' // Hoặc import từ 'vitest'
import app from '.' // Instance ứng dụng Hono của bạn
describe('Hono App with app.request()', () => {
it('GET / should return "Hello Hono!"', async () => {
const res = await app.request('/')
expect(res.status).toBe(200)
expect(await res.text()).toBe('Hello Hono!') // Điều chỉnh cho phản hồi gốc thực tế của bạn
})
it('GET /posts/:id should return a specific post', async () => {
// Giả sử ứng dụng của bạn có route như app.get('/posts/:id', ...)
// và cho bài kiểm thử này, nó có thể trả về { id: '123', title: 'Test Post 123' }
const res = await app.request('/posts/123')
expect(res.status).toBe(200)
const data = await res.json()
expect(data.id).toBe('123')
// expect(data.title).toBe('Post 123') // Hoặc dựa trên logic của ứng dụng bạn
})
it('POST /posts should create a new post', async () => {
const newPost = { title: 'My New Post', content: 'Awesome content here.' }
const res = await app.request('/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newPost),
})
expect(res.status).toBe(201) // Giả sử POST /posts của bạn trả về 201
const responseData = await res.json()
expect(responseData.message).toBe('Post created successfully!')
expect(responseData.data.title).toBe(newPost.title)
})
it('GET /api/v1/me without API key should be 401', async () => {
// Giả sử middleware API key từ ví dụ trước đó đang hoạt động trên route này
const res = await app.request('/api/v1/me')
expect(res.status).toBe(401)
})
it('GET /api/v1/me with correct API key should be 200', async () => {
const res = await app.request('/api/v1/me', {
headers: {
'X-API-KEY': 'supersecretapikey' // Sử dụng key từ middleware của bạn
}
})
expect(res.status).toBe(200)
const data = await res.json();
expect(data.user).toBe('Authenticated User')
})
})
Sử dụng Helper testClient()
để Kiểm thử An toàn Kiểu
Để có độ an toàn kiểu tốt hơn nữa, đặc biệt nếu bạn đang sử dụng khả năng RPC của Hono hoặc có các schema route được định nghĩa rõ ràng, Hono cung cấp một helper testClient
(từ hono/testing
). Client này được gõ kiểu dựa trên các route của ứng dụng Hono của bạn, mang lại cho bạn tính năng tự động hoàn thành và kiểm tra kiểu trong các bài kiểm thử của bạn.
Lưu ý Quan trọng cho Suy luận Kiểu của testClient
:
Để testClient
suy luận kiểu chính xác, bạn phải định nghĩa các route của mình bằng cách xâu chuỗi các phương thức trực tiếp trên instance Hono
(ví dụ: const app = new Hono().get(...).post(...)
) hoặc export kiểu route nếu sử dụng RPC. Nếu bạn định nghĩa các route riêng biệt (ví dụ: const app = new Hono(); app.get(...)
), suy luận kiểu cho testClient
có thể bị hạn chế.
// src/app-for-test-client.ts
// Để minh họa testClient, hãy định nghĩa một ứng dụng với các route được xâu chuỗi
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator' // Ví dụ cho query params gõ kiểu
import { z } from 'zod'
const appWithChainedRoutes = new Hono()
.get('/search',
zValidator('query', z.object({ q: z.string(), limit: z.coerce.number().optional() })),
(c) => {
const { q, limit } = c.req.valid('query')
return c.json({ query: q, limit: limit || 10, results: [`result for ${q} 1`, `result for ${q} 2`] })
}
)
.post('/submit',
zValidator('json', z.object({ name: z.string(), email: z.string().email() })),
async (c) => {
const data = c.req.valid('json')
return c.json({ message: `Received data for ${data.name}`, data }, 201)
}
);
export default appWithChainedRoutes;
// src/app-for-test-client.test.ts
import { describe, expect, it } from 'bun:test'
import { testClient } from 'hono/testing'
import appWithChainedRoutes from './app-for-test-client' // Import ứng dụng
describe('Hono App with testClient', () => {
const client = testClient(appWithChainedRoutes)
it('GET /search should return typed results', async () => {
const res = await client.search.$get({
query: { q: 'hono rocks' }
})
expect(res.status).toBe(200)
const data = await res.json()
expect(data.query).toBe('hono rocks')
expect(data.results.length).toBeGreaterThan(0)
})
it('GET /search with limit should respect it', async () => {
const res = await client.search.$get({
query: { q: 'hono with limit', limit: '5' } // Query params ban đầu là chuỗi
})
expect(res.status).toBe(200)
const data = await res.json()
expect(data.limit).toBe(5) // Zod validator đã chuyển đổi nó thành số
})
it('POST /submit should accept valid data', async () => {
const payload = { name: 'Test User', email: 'test@example.com' }
// Đối với POST, PUT, v.v. với body JSON, là client.path.$post({ json: payload })
const res = await client.submit.$post({
json: payload
})
expect(res.status).toBe(201)
const data = await res.json()
expect(data.message).toBe(`Received data for ${payload.name}`)
expect(data.data.email).toBe(payload.email)
});
it('POST /submit with headers', async () => {
const payload = { name: 'Test User Headers', email: 'testheaders@example.com' }
const res = await client.submit.$post({
json: payload
}, {
headers: {
'X-Custom-Test-Header': 'Testing123'
}
})
expect(res.status).toBe(201)
// Bạn thường sẽ có một route/middleware đọc header này để xác minh
});
})
Để chạy bài kiểm thử này, giả sử bạn đã cài đặt @hono/zod-validator
và zod
(bun add @hono/zod-validator zod
):
bun test src/app-for-test-client.test.ts
testClient
này cung cấp trải nghiệm phát triển tốt hơn nhiều cho việc kiểm thử, đặc biệt đối với các API có schema được định nghĩa.
Kết luận
Hono mang đến một cách tiếp cận mới mẻ cho phát triển web với sự kết hợp giữa tốc độ, thiết kế nhẹ nhàng và các tính năng thân thiện với nhà phát triển. Hướng dẫn này đã đề cập đến những điều cơ bản để bạn bắt đầu, nhưng còn nhiều điều nữa để khám phá:
- Các kỹ thuật định tuyến nâng cao
- Các mẫu middleware phức tạp hơn
- Server-side rendering với JSX
- Tích hợp với cơ sở dữ liệu
- API kiểu RPC với an toàn kiểu sử dụng Hono Client
- Triển khai trên các nền tảng khác nhau
Khi bạn tiếp tục hành trình của mình với Hono, hãy tham khảo tài liệu chính thức và kho ví dụ để có thêm thông tin chi tiết và cảm hứng.
Bức tranh phát triển web không ngừng thay đổi, và Hono đại diện cho một cách tiếp cận hiện đại tuân thủ Web Standards, ưu tiên hiệu suất và hoạt động trên hệ sinh thái runtime JavaScript đang phát triển. Cho dù bạn đang xây dựng các API đơn giản hay các ứng dụng phức tạp, Hono cung cấp các công cụ bạn cần mà không có gánh nặng không cần thiết.
Bắt đầu nhỏ, thử nghiệm và xem các ứng dụng của bạn phát triển với Hono – framework web nhẹ, cực nhanh đúng như tên gọi đầy lửa của nó. 🔥
Bạn muốn một nền tảng Tích hợp, Tất cả trong Một để Nhóm Phát triển của bạn làm việc cùng nhau với năng suất tối đa?
Apidog đáp ứng mọi yêu cầu của bạn và thay thế Postman với mức giá phải chăng hơn nhiều!