Express is dated. Fastify is Node-only. H3 is a minimal, ultrafast HTTP framework that runs on Node.js, Bun, Deno, Cloudflare Workers, and every serverless platform.
What Is H3?
H3 is a minimal HTTP framework from the UnJS ecosystem. It powers Nuxt 3 and Nitro but works standalone too. Same code runs everywhere — Node.js, edge runtimes, serverless.
Quick Start
npm install h3
import { createApp, createRouter, defineEventHandler } from 'h3';
const app = createApp();
const router = createRouter();
router.get('/hello', defineEventHandler(() => {
return { message: 'Hello World!' };
}));
router.post('/users', defineEventHandler(async (event) => {
const body = await readBody(event);
return { id: 1, ...body };
}));
app.use(router);
Why H3 Over Express
| Feature | H3 | Express | Fastify |
|---|---|---|---|
| Runtime | Any | Node.js | Node.js |
| TypeScript | Native | Manual | Plugin |
| Bundle size | 20KB | 200KB | 350KB |
| Edge support | Yes | No | No |
| JSON handling | Auto | Manual | Auto |
| Performance | Ultra-fast | Moderate | Fast |
Utilities
H3 has built-in utilities for common patterns:
import {
readBody, // Parse request body
getQuery, // Parse query parameters
getRouterParam, // Get route params
getHeader, // Get request header
setHeader, // Set response header
setCookie, // Set cookie
getCookie, // Get cookie
createError, // Create HTTP error
sendRedirect, // Send redirect
readMultipartFormData, // Handle file uploads
} from 'h3';
router.get('/users/:id', defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id');
const query = getQuery(event); // { page: '1', limit: '10' }
const token = getHeader(event, 'authorization');
if (!token) {
throw createError({ statusCode: 401, message: 'Unauthorized' });
}
return { id, ...query };
}));
Middleware
app.use(defineEventHandler((event) => {
// Runs for every request
console.log(`${event.method} ${event.path}`);
}));
// CORS middleware
app.use(defineEventHandler((event) => {
setHeader(event, 'Access-Control-Allow-Origin', '*');
}));
WebSocket Support
import { defineWebSocketHandler } from 'h3';
router.get('/ws', defineWebSocketHandler({
open(peer) {
console.log('Connected:', peer);
},
message(peer, message) {
peer.send(`Echo: ${message}`);
},
close(peer) {
console.log('Disconnected:', peer);
},
}));
Deploy Anywhere
// Node.js
import { toNodeHandler } from 'h3';
import { createServer } from 'http';
createServer(toNodeHandler(app)).listen(3000);
// Bun
export default { fetch: toWebHandler(app) };
// Cloudflare Workers
export default { fetch: toWebHandler(app) };
// Deno
Deno.serve(toWebHandler(app));
Get Started
- Documentation
- GitHub — 4K+ stars
- UnJS ecosystem
Building APIs? My Apify scrapers power data-driven backends. Custom solutions: spinov001@gmail.com
Top comments (0)