Vafast
1,837,294 reqs/sHono
740,451
Measured in requests/second. Result from TechEmpower Benchmark Round 23 (2025-02-24) in JSON serialization
This guide is for Hono users who want to see the differences between Hono and Vafast, including syntax, and how to migrate an application from Hono to Vafast by example.
Hono is a lightweight, ultra-fast web framework designed for edge runtimes, supporting many platforms.
Vafast is a high-performance TypeScript web framework that runs on Node.js, Bun and other runtimes, focused on type safety, a middleware system and performance. It's designed for simplicity and developer friendliness, with full TypeScript support.
Thanks to radix tree route matching and JIT-compiled validators, Vafast is significantly faster than Hono.
740,451
Measured in requests/second. Result from TechEmpower Benchmark Round 23 (2025-02-24) in JSON serialization
Both Hono and Vafast define routes with configuration objects, but Vafast offers a more structured API and better type safety.
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello World')
})
app.post('/user/:id', async (c) => {
const id = c.req.param('id')
const body = await c.req.json()
return c.json({ id, name: body.name })
})
export default appHono uses
c(context) as the request and response object
import { Server, defineRoute, defineRoutes } from 'vafast'
const routes = defineRoutes([
defineRoute({
method: 'GET',
path: '/',
handler: () => 'Hello World'
}),
defineRoute({
method: 'POST',
path: '/user/:id',
handler: async ({ params, req }) => {
const body = await req.json()
return { id: params.id, name: body.name }
}
})
])
const server = new Server(routes)
export default { fetch: server.fetch }Vafast defines routes with configuration objects, with type safety and middleware support
Hono uses chained method calls:
app.get('/users', (c) => { ... })
app.post('/users', (c) => { ... })Vafast uses an array of configuration objects:
const routes = defineRoutes([
defineRoute({
method: 'GET',
path: '/users',
handler: () => { ... }
}),
defineRoute({
method: 'POST',
path: '/users',
handler: () => { ... }
})
])Hono uses the c (context) object:
app.get('/user/:id', (c) => {
const id = c.req.param('id')
const query = c.req.query()
return c.json({ id, query })
})Vafast uses destructured parameters:
defineRoute({
method: 'GET',
path: '/user/:id',
handler: ({ params, query }) => {
return { id: params.id, query }
})
}Hono uses app.use() and route-level middleware:
app.use('*', async (c, next) => {
console.log(`${c.req.method} ${c.req.url}`)
await next()
})
app.get('/admin', authMiddleware, (c) => {
return c.text('Admin Panel')
})Vafast supports global and route-level middleware:
import { defineMiddleware } from 'vafast'
const loggingMiddleware = defineMiddleware(async (req, next) => {
console.log(`${req.method} ${req.url}`)
return await next()
})
const routes = defineRoutes([
defineRoute({
method: 'GET',
path: '/admin',
handler: () => 'Admin Panel',
middleware: [authMiddleware]
})
])
const server = new Server(routes)
server.use(loggingMiddleware)Hono uses Zod for validation:
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const userSchema = z.object({
name: z.string().min(1),
email: z.string().email()
})
app.post('/users', zValidator('json', userSchema), (c) => {
const body = c.req.valid('json')
return c.json(createUser(body))
})Vafast uses TypeBox for validation:
import { defineRoute, Type } from 'vafast'
const userSchema = Type.Object({
name: Type.String({ minLength: 1 }),
email: Type.String({ format: 'email' })
})
defineRoute({
method: 'POST',
path: '/users',
schema: { body: userSchema },
handler: ({ body }) => {
return createUser(body)
}
})How the new framework works:
- Schema validation is defined in the route's
schemafield- The handler receives validated data directly, with automatic type inference
Hono uses an error handler:
app.onError((err, c) => {
console.error(`${err}`)
return c.text('Custom Error Message', 500)
})Vafast has a built-in errorHandler; just throw err.xxx() in the handler:
import { err } from 'vafast'
handler: ({ params }) => {
if (!found) throw err.notFound('Not found')
return data
}The framework injects
errorHandlerautomatically, so there's no need to write try/catch middleware.
npm install vafastConvert Hono route definitions into Vafast's configuration object format:
// Hono style
app.get('/api/users', (c) => {
const users = getUsers()
return c.json(users)
})
// Vafast style
defineRoute({
method: 'GET',
path: '/api/users',
handler: () => {
return getUsers()
}
})Convert Hono middleware into the Vafast middleware format:
// Hono middleware
app.use('*', async (c, next) => {
const token = c.req.header('authorization')
if (!token) {
return c.text('Unauthorized', 401)
}
await next()
})
// Vafast middleware
import { defineMiddleware, json } from 'vafast'
const authMiddleware = defineMiddleware(async (req, next) => {
const token = req.headers.get('authorization')
if (!token) {
return json({ error: 'Unauthorized' }, 401)
}
return await next()
})// Hono validation
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const userSchema = z.object({
name: z.string().min(1),
email: z.string().email()
})
app.post('/users', zValidator('json', userSchema), (c) => {
const body = c.req.valid('json')
return c.json(createUser(body))
})
// Vafast validation
import { Type } from 'vafast'
const userSchema = Type.Object({
name: Type.String({ minLength: 1 }),
email: Type.String({ format: 'email' })
})
defineRoute({
method: 'POST',
path: '/users',
schema: {
body: userSchema
},
handler: ({ body }) => {
return createUser(body)
}
})// Hono error handling
app.onError((err, c) => {
return c.text('Something went wrong', 500)
})
// Vafast: built-in errorHandler; throw err.xxx() in the handler
import { err } from 'vafast'
handler: ({ params }) => {
if (!found) throw err.notFound('Not found')
return data
}import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const app = new Hono()
app.use('*', cors())
app.use('*', logger())
const userSchema = z.object({
name: z.string().min(1),
email: z.string().email()
})
app.get('/users', (c) => {
const users = getUsers()
return c.json(users)
})
app.post('/users', zValidator('json', userSchema), (c) => {
const body = c.req.valid('json')
const user = createUser(body)
c.status(201)
return c.json(user)
})
app.get('/users/:id', (c) => {
const id = c.req.param('id')
const user = getUserById(id)
if (!user) {
c.status(404)
return c.json({ error: 'User not found' })
}
return c.json(user)
})
export default appimport { Server, defineRoute, defineRoutes, Type, json, err } from 'vafast'
import { cors } from '@vafast/cors'
const userSchema = Type.Object({
name: Type.String({ minLength: 1 }),
email: Type.String({ format: 'email' })
})
const routes = defineRoutes([
defineRoute({
method: 'GET',
path: '/users',
handler: () => getUsers()
}),
defineRoute({
method: 'POST',
path: '/users',
schema: { body: userSchema },
handler: ({ body }) => json(createUser(body), 201)
}),
defineRoute({
method: 'GET',
path: '/users/:id',
handler: ({ params }) => {
const user = getUserById(params.id)
if (!user) {
throw err.notFound('User not found')
}
return user
}
})
])
const server = new Server(routes)
server.use(cors())
export default { fetch: server.fetch }| Feature | Hono | Vafast |
|---|---|---|
| Type safety | ⚠️ Requires extra setup | ✅ Full TypeScript support |
| Performance | ✅ High performance | Very high performance |
| Validation | ✅ Zod support | ✅ TypeBox support |
| Middleware system | ✅ Flexible | ✅ Flexible and extensible |
| Route definitions | ⚠️ Chained calls | ✅ Configuration objects |
| Error handling | ✅ Error handler | ✅ Middleware chain |
| Bun support | ⚠️ Requires adaptation | ✅ Native |
Now that you know how to migrate from Hono to Vafast, we suggest you:
If you run into any issues during migration, feel free to ask for help on GitHub Issues.