Using with H3
Quick Start
CommonJS
const { H3, serve } = require('h3');
const Router = require('clear-router/h3');
const app = new H3();
Router.get('/hello', () => {
return 'Hello World';
});
Router.apply(app);
serve(app, { port: 3000 });ESM
import { H3, serve } from 'h3';
import Router from 'clear-router/h3';
const app = new H3();
Router.get('/hello', ({ res }) => {
res.send('Hello World');
});
await Router.apply(app);
serve(app, { port: 3000 });TypeScript
import express from 'express';
import Router from 'clear-router/h3';
const app = new H3();
Router.get('/hello', ({ res }) => {
res.send('Hello World');
});
await Router.apply(app);
serve(app, { port: 3000 });Usage Examples
Basic Route
Router.get('/hello', () => {
return 'Hello World';
});With Middleware
const authMiddleware = (evt, next) => {
// auth logic
next();
};
Router.post('/secure', () => 'Protected', [authMiddleware]);Method Override
Clear Router supports HTTP method override for POST requests using form body keys or headers.
Default override keys:
- Body:
_method - Header:
X-HTTP-Method
Router.put('/users/:id', (event) => {
return { method: event.req.method, id: event.context.params.id };
});
// POST /users/12 with body { "_method": "PUT" }Custom keys are supported:
Router.configure({
methodOverride: {
bodyKeys: ['_method', 'method'],
headerKeys: ['x-http-method', 'x-method-override'],
},
});Disable override behavior:
Router.configure({ methodOverride: { enabled: false } });Request Body Access via ctx.req.getBody()
clear-router patches H3 requests with ctx.req.getBody() so body access is consistent in handlers and controllers.
- Always available in route handlers.
- Returns parsed JSON/form/multipart body when present.
- Returns
{}when the request has no body.
Router.post('/users', (ctx) => {
const body = ctx.req.getBody();
return { hasName: Boolean(body.name) };
});
Router.get('/status', (ctx) => {
// Safe even for GET requests without a body
return { body: ctx.req.getBody() }; // {}
});Controller Binding
class UserController {
index() {
return 'User List';
}
}
Router.get('/users', [UserController, 'index']);Class-based handlers will auto-bind to static or instance methods.
Custom Controllers (Extending the Base Controller)
For advanced use cases, you can create project-specific controller base classes that extend the clear-router Controller.
// Example app-level base controller
class AppController extends Controller<H3Event> {
ok(data: any) {
return { success: true, data };
}
get userId() {
return this.params?.id;
}
}
class UserController extends AppController {
show() {
return this.ok({ id: this.userId, query: this.query });
}
}
Router.get('/users/:id', [UserController, 'show']);Benefits
- Centralizes shared response helpers and controller utilities.
- Reuses hydrated request data (
this.ctx,this.body,this.query,this.params,this.clearRequest) consistently. - Reduces repeated boilerplate across controllers.
- Makes controller behavior easier to standardize and test.
Handler Arguments and ClearRequest
H3 handlers are invoked with:
ctx: H3 eventclearRequest:ClearRequest | undefined
Router.put('/users/:id', (event, clearRequest) => {
return {
method: event.req.method,
hasClearRequest: Boolean(clearRequest),
};
});For controller instance handlers ([ControllerClass, 'method']), router hydration includes:
this.body(fromctx.req.getBody())this.query(query params)this.params(route params)this.clearRequest(normalized request wrapper)
API Resource Binding
You can also bind routes to API resources:
class UserController {
index({ res }) {
return [{ name: 'User 1' }, { name: 'User 2' }];
}
show({ res }) {
returnn { name: 'User 1' };
}
create({ res }) {
return 'User created';
}
update({ res }) {
return 'User updated';
}
destroy({ res }) {
return 'User deleted';
}
}
Router.apiResource('/users', UserController);Grouped Routes
Router.group('/admin', () => {
Router.get('/dashboard', () => 'Admin Panel');
});Async group callbacks are also supported:
await Router.group('/api', async () => {
await loadRoutes();
Router.get('/status', () => ({ ok: true }));
});With middleware:
Router.group(
'/secure',
() => {
Router.get('/data', () => 'Secure Data');
},
[authMiddleware],
);Global Middleware Scope
Router.middleware([authMiddleware], () => {
Router.get('/profile', () => 'My Profile');
});Multiple HTTP Methods
Router.add(['get', 'post'], '/handle', ({ req }) => {
return `Method: ${req.method}`;
});Inspecting Routes
Router.get('/hello', ({ res }) => 'Hello');
Router.post('/world', ({ res }) => 'World');
const allRoutes = Router.allRoutes();
console.log(allRoutes);
// Output:
// [
// { methods: ['get'], path: '/hello', middlewareCount: 0, handlerType: 'function' },
// { methods: ['post'], path: '/world', middlewareCount: 0, handlerType: 'function' }
// ]API Reference
See API for complete API documentation.
Middleware Execution Order
[ Global Middleware ] → [ Group Middleware ] → [ Route Middleware ]Handler Execution
- If function: executed directly
- If [Controller, 'method']: auto-instantiated (if needed), method is called
- First argument is always H3 event context
- Second argument is
clearRequest(defined for controller handlers)
Testing
npm test # Run all tests
npm run test:esm # Test ESM
npm run test:ts # Test TypeScriptSee Testing for detailed testing guide.
Examples
npm run example # CommonJS example
npm run example:esm # ESM example
npm run example:ts # TypeScript exampleCheck example/ directory for full working demos.