or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-features.mdcore-framework.mderror-handling.mdevent-handling.mdhandlers-middleware.mdindex.mdrequest-processing.mdresponse-handling.mdruntime-adapters.mdweb-utilities.md
tile.json

handlers-middleware.mddocs/

Handler and Middleware System

H3's handler and middleware system provides type-safe handler definition with middleware support, validation, lazy loading, and dynamic routing capabilities.

Capabilities

Handler Definition

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);
    }
  ]
});

Lazy Event Handlers

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);

Dynamic Event Handlers

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" }));
}

Validated Handlers (Experimental)

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 };
  }
});

Middleware System

Middleware Definition

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);

Lifecycle Hooks

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);

Handler Types

Core Handler Types

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;

Request and Response Types

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;

Advanced Handler Patterns

Middleware Composition

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);

Handler Factories

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);