H3's handler and middleware system provides type-safe handler definition with middleware support, validation, lazy loading, and dynamic routing capabilities.
Define type-safe event handlers with optional middleware integration.
/**
* Define an event handler function
* @param handler - Event handler function
* @returns Event handler with fetch method
*/
function defineHandler<Req, Res>(handler: EventHandler<Req, Res>): EventHandlerWithFetch<Req, Res>;
/**
* Define an event handler with middleware and hooks
* @param def - Handler definition object with middleware
* @returns Event handler with fetch method
*/
function defineHandler<Req, Res>(def: EventHandlerObject<Req, Res>): EventHandlerWithFetch<Req, Res>;Usage Examples:
import { defineHandler } from "h3";
// Simple handler
const simpleHandler = defineHandler(async (event) => {
return { message: "Hello World" };
});
// Handler with middleware
const handlerWithMiddleware = defineHandler({
onRequest: [
(event) => {
console.log(`Request: ${event.method} ${event.url}`);
}
],
handler: async (event) => {
const body = await readBody(event);
return { received: body };
},
onResponse: [
(response, event) => {
console.log(`Response sent for ${event.url}`);
}
],
onError: [
(error, event) => {
console.error(`Error in ${event.url}:`, error);
}
]
});Create handlers that are loaded on-demand for better performance.
/**
* Create a lazy-loaded event handler
* @param load - Function that returns handler when called
* @returns Event handler that loads on first request
*/
function defineLazyEventHandler(
load: () => Promise<EventHandler> | EventHandler
): EventHandlerWithFetch;Usage Examples:
import { defineLazyEventHandler } from "h3";
// Lazy load handler module
const lazyHandler = defineLazyEventHandler(async () => {
const { heavyHandler } = await import("./heavy-handler");
return heavyHandler;
});
// Lazy load with dynamic imports
const dynamicHandler = defineLazyEventHandler(async () => {
const module = await import("./api/users");
return module.default;
});
// Use in routes
app.get("/heavy", lazyHandler);
app.get("/api/users", dynamicHandler);Create handlers that can be changed at runtime.
/**
* Create a dynamic event handler that can be updated at runtime
* @param initial - Optional initial handler
* @returns Dynamic handler interface
*/
function dynamicEventHandler(initial?: EventHandler): DynamicEventHandler;
interface DynamicEventHandler {
/**
* Update the handler function
* @param handler - New handler function
*/
set(handler: EventHandler): void;
/**
* The current handler function
*/
handler: EventHandler;
}Usage Examples:
import { dynamicEventHandler } from "h3";
// Create dynamic handler
const dynamic = dynamicEventHandler(() => ({ message: "Initial handler" }));
// Update handler at runtime
dynamic.set(async (event) => {
const data = await fetchExternalData();
return { data };
});
// Use in application
app.get("/dynamic", dynamic.handler);
// Update again based on conditions
if (process.env.NODE_ENV === "development") {
dynamic.set(() => ({ message: "Development mode" }));
}Define handlers with built-in validation support.
/**
* Define a handler with validation (experimental)
* @param def - Handler definition with validation schema
* @returns Validated event handler
*/
function defineValidatedHandler<Schema>(def: {
handler: EventHandler;
validate?: {
body?: Schema;
query?: Schema;
params?: Schema;
};
}): EventHandlerWithFetch;Usage Examples:
import { defineValidatedHandler } from "h3";
import { z } from "zod"; // Example with Zod
// Handler with validation
const validatedHandler = defineValidatedHandler({
validate: {
body: z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().min(18)
}),
query: z.object({
page: z.string().transform(Number).optional(),
limit: z.string().transform(Number).optional()
})
},
handler: async (event) => {
// Body and query are automatically validated
const body = await readValidatedBody(event, (data) => data);
const query = getValidatedQuery(event, (data) => data);
return { body, query };
}
});Define reusable middleware functions.
/**
* Define a middleware function
* @param input - Middleware function
* @returns Middleware function
*/
function defineMiddleware(input: Middleware): Middleware;Usage Examples:
import { defineMiddleware } from "h3";
// Logging middleware
const loggingMiddleware = defineMiddleware((event) => {
console.log(`${event.method} ${event.url} - ${new Date().toISOString()}`);
});
// Authentication middleware
const authMiddleware = defineMiddleware(async (event) => {
const token = getHeader(event, "authorization");
if (!token) {
throw HTTPError.status(401, "Authorization required");
}
const user = await verifyToken(token);
event.context.user = user;
});
// Rate limiting middleware
const rateLimitMiddleware = defineMiddleware((event) => {
const ip = getRequestIP(event);
if (isRateLimited(ip)) {
throw HTTPError.status(429, "Too Many Requests");
}
recordRequest(ip);
});
// Use middleware
app.use(loggingMiddleware);
app.use("/api", authMiddleware);
app.use(rateLimitMiddleware);Middleware hooks for different phases of request processing.
/**
* Register middleware to run on every request
* @param hook - Function to execute on request
* @returns Middleware function
*/
function onRequest(hook: (event: H3Event) => void | Promise<void>): Middleware;
/**
* Register middleware to run on every response
* @param hook - Function to execute on response
* @returns Middleware function
*/
function onResponse(
hook: (response: Response, event: H3Event) => MaybePromise<void | Response>
): Middleware;
/**
* Register middleware to run on errors
* @param hook - Function to execute on error
* @returns Middleware function
*/
function onError(
hook: (error: HTTPError, event: H3Event) => MaybePromise<void | unknown>
): Middleware;Usage Examples:
import { onRequest, onResponse, onError } from "h3";
// Request timing
const timingMiddleware = onRequest((event) => {
event.context.startTime = Date.now();
});
// Add response headers
const headersMiddleware = onResponse((response, event) => {
const duration = Date.now() - event.context.startTime;
response.headers.set("X-Response-Time", `${duration}ms`);
response.headers.set("X-Powered-By", "H3");
return response;
});
// Error handling
const errorMiddleware = onError((error, event) => {
console.error(`Error in ${event.url}:`, error);
// Return custom error response
if (error.status === 404) {
return { error: "Not Found", path: event.url.pathname };
}
});
// Register middleware
app.use(timingMiddleware);
app.use(headersMiddleware);
app.use(errorMiddleware);Type definitions for event handlers and middleware.
/**
* Event handler function type
*/
type EventHandler<RequestT = any, ResponseT = any> = (
event: H3Event
) => ResponseT | Promise<ResponseT>;
/**
* Event handler with fetch method
*/
interface EventHandlerWithFetch<RequestT = any, ResponseT = any> extends EventHandler<RequestT, ResponseT> {
/**
* Fetch-compatible interface
*/
fetch: (request: ServerRequest, context?: H3EventContext) => Promise<Response>;
}
/**
* Handler object with middleware hooks
*/
interface EventHandlerObject<RequestT = any, ResponseT = any> {
/**
* Main handler function
*/
handler: EventHandler<RequestT, ResponseT>;
/**
* Middleware to run before handler
*/
onRequest?: Middleware[];
/**
* Middleware to run after handler
*/
onResponse?: Middleware[];
/**
* Middleware to run on errors
*/
onError?: Middleware[];
}
/**
* Middleware function type
*/
type Middleware = (event: H3Event) => void | Promise<void>;
/**
* Lazy event handler type
*/
type LazyEventHandler = () => Promise<EventHandler> | EventHandler;Types for inferring request and response types from handlers.
/**
* Handler request interface
*/
interface EventHandlerRequest {
body?: any;
query?: Record<string, string>;
params?: Record<string, string>;
headers?: Record<string, string>;
}
/**
* Handler response type
*/
type EventHandlerResponse<T = any> = T | Promise<T>;
/**
* Type helper for inferring event input types
*/
type InferEventInput<Key extends keyof EventHandlerRequest, Event, T = any> =
Event extends { [K in Key]: infer U } ? U : T;Compose multiple middleware functions into reusable units.
// Example composition pattern
function composeMiddleware(...middlewares: Middleware[]): Middleware {
return defineMiddleware(async (event) => {
for (const middleware of middlewares) {
await middleware(event);
}
});
}Usage Examples:
// Compose authentication and authorization
const authFlow = composeMiddleware(
authMiddleware, // Authenticate user
permissionsMiddleware, // Check permissions
rateLimitMiddleware // Apply rate limiting
);
// Use composed middleware
app.use("/admin", authFlow);
// Compose request processing
const requestProcessing = composeMiddleware(
corsMiddleware,
loggingMiddleware,
validationMiddleware
);
app.use(requestProcessing);Create handler factories for common patterns.
// Example handler factory
function createCRUDHandler<T>(resource: string) {
return {
list: defineHandler(() => findAll(resource)),
get: defineHandler((event) => {
const id = getRouterParam(event, "id");
return findById(resource, id);
}),
create: defineHandler(async (event) => {
const data = await readBody(event);
return create(resource, data);
}),
update: defineHandler(async (event) => {
const id = getRouterParam(event, "id");
const data = await readBody(event);
return update(resource, id, data);
}),
delete: defineHandler((event) => {
const id = getRouterParam(event, "id");
return remove(resource, id);
})
};
}Usage Examples:
// Create CRUD handlers for users
const userHandlers = createCRUDHandler("users");
// Register routes
app.get("/users", userHandlers.list);
app.get("/users/:id", userHandlers.get);
app.post("/users", userHandlers.create);
app.put("/users/:id", userHandlers.update);
app.delete("/users/:id", userHandlers.delete);