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

response-handling.mddocs/

Response Handling

Response utilities for sending different content types, redirects, status codes, streaming responses, and converting values to standard Response objects.

Capabilities

Response Creation

Convert various return values to standard Response objects.

/**
 * Convert any value to a Response object
 * @param val - Value to convert (object, string, Response, etc.)
 * @param event - H3 event context
 * @param config - Optional H3 configuration
 * @returns Response object or Promise resolving to Response
 */
function toResponse(val: unknown, event: H3Event, config?: H3Config): Response | Promise<Response>;

Usage Examples:

import { toResponse } from "h3";

// Convert various types
const handler = defineHandler(async (event) => {
  // Objects become JSON responses
  const jsonResponse = await toResponse({ message: "Hello" }, event);
  
  // Strings become text responses
  const textResponse = await toResponse("Hello World", event);
  
  // Numbers become text responses
  const numberResponse = await toResponse(42, event);
  
  // Arrays become JSON responses
  const arrayResponse = await toResponse([1, 2, 3], event);
  
  // Response objects pass through
  const customResponse = await toResponse(
    new Response("Custom", { status: 201 }),
    event
  );
  
  return jsonResponse;
});

Status and Redirect Responses

Send status codes, redirects, and empty responses.

/**
 * Send a redirect response
 * @param event - H3 event
 * @param location - Redirect URL
 * @param code - HTTP status code (default: 302)
 * @returns Redirect location string
 */
function redirect(event: H3Event, location: string, code?: number): string;

/**
 * Send an empty response with optional status code
 * @param event - H3 event
 * @param code - HTTP status code (default: 204)
 * @returns Empty Response object
 */
function noContent(event: H3Event, code?: number): Response;

Usage Examples:

import { redirect, noContent } from "h3";

// Redirect responses
const redirectHandler = defineHandler((event) => {
  const { type } = getQuery(event);
  
  if (type === "permanent") {
    return redirect(event, "/new-location", 301);
  } else {
    return redirect(event, "/temporary-location", 302);
  }
});

// Empty responses
const deleteHandler = defineHandler((event) => {
  const id = getRouterParam(event, "id");
  deleteResource(id);
  
  return noContent(event, 204); // No Content
});

// Custom status codes
const acceptedHandler = defineHandler((event) => {
  processAsync(event);
  return noContent(event, 202); // Accepted
});

Content Type Responses

Send specific content types with proper headers.

/**
 * Send HTML content with proper content-type header
 * @param event - H3 event
 * @param content - HTML string content
 * @returns HTML content string
 */
function html(event: H3Event, content: string): string;

Usage Examples:

import { html } from "h3";

// HTML responses
const pageHandler = defineHandler((event) => {
  const page = `
    <!DOCTYPE html>
    <html>
      <head><title>My Page</title></head>
      <body>
        <h1>Welcome</h1>
        <p>This is a dynamic page.</p>
      </body>
    </html>
  `;
  
  return html(event, page);
});

// Dynamic HTML
const dynamicHandler = defineHandler((event) => {
  const { name } = getQuery(event);
  const content = `
    <html>
      <body>
        <h1>Hello, ${name || 'Guest'}!</h1>
      </body>
    </html>
  `;
  
  return html(event, content);
});

Streaming Responses

Stream data using async iterables and generators.

/**
 * Stream iterable content as a response
 * @param event - H3 event
 * @param iterable - Async iterable or generator
 * @param options - Streaming options
 * @returns ReadableStream for streaming response
 */
function iterable<Value, Return = any>(
  event: H3Event,
  iterable: IterationSource<Value, Return>,
  options?: IterableOptions<Value>
): ReadableStream;

interface IterableOptions<Value = any> {
  /**
   * Serializer function for values
   */
  serializer?: (value: Value) => string | Uint8Array;
  
  /**
   * Separator between values
   */
  separator?: string;
  
  /**
   * Content type header
   */
  contentType?: string;
}

type IterationSource<Value, Return = any> = 
  | Iterable<Value>
  | AsyncIterable<Value>
  | Generator<Value, Return>
  | AsyncGenerator<Value, Return>;

Usage Examples:

import { iterable } from "h3";

// Stream array data
const streamArrayHandler = defineHandler((event) => {
  const data = [1, 2, 3, 4, 5];
  
  return iterable(event, data, {
    serializer: (value) => JSON.stringify(value),
    separator: "\n",
    contentType: "application/x-ndjson"
  });
});

// Stream with async generator
const streamAsyncHandler = defineHandler((event) => {
  async function* generateData() {
    for (let i = 0; i < 10; i++) {
      await new Promise(resolve => setTimeout(resolve, 100));
      yield { id: i, timestamp: Date.now() };
    }
  }
  
  return iterable(event, generateData(), {
    serializer: (value) => JSON.stringify(value) + "\n"
  });
});

// Stream large dataset
const streamDatabaseHandler = defineHandler((event) => {
  async function* streamRecords() {
    const cursor = database.cursor();
    
    for await (const record of cursor) {
      yield record;
    }
  }
  
  return iterable(event, streamRecords(), {
    serializer: JSON.stringify,
    separator: ",\n",
    contentType: "application/json"
  });
});

// Stream file lines
const streamFileHandler = defineHandler((event) => {
  const filePath = getRouterParam(event, "file");
  
  async function* readLines() {
    const file = await fs.open(filePath, "r");
    const stream = file.createReadStream();
    const reader = stream.getReader();
    
    try {
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        yield new TextDecoder().decode(value);
      }
    } finally {
      reader.releaseLock();
      await file.close();
    }
  }
  
  return iterable(event, readLines());
});

Early Hints

Send HTTP/1.1 103 Early Hints for performance optimization.

/**
 * Write HTTP/1.1 103 Early Hints response
 * @param event - H3 event
 * @param hints - Object with link relations and URLs
 * @returns void or Promise<void>
 */
function writeEarlyHints(
  event: H3Event,
  hints: Record<string, string>
): void | Promise<void>;

Usage Examples:

import { writeEarlyHints } from "h3";

// Send early hints for performance
const pageWithHintsHandler = defineHandler(async (event) => {
  // Send early hints to help browser preload resources
  await writeEarlyHints(event, {
    "preload": "</styles.css>; rel=preload; as=style",
    "preconnect": "<https://api.example.com>; rel=preconnect"
  });
  
  // Process the actual response
  const data = await fetchPageData();
  
  return html(event, `
    <html>
      <head>
        <link rel="stylesheet" href="/styles.css">
      </head>
      <body>
        <h1>${data.title}</h1>
      </body>
    </html>
  `);
});

// Early hints for API responses
const apiWithHintsHandler = defineHandler(async (event) => {
  await writeEarlyHints(event, {
    "preload": "</api/user>; rel=preload; as=fetch",
    "preload": "</api/settings>; rel=preload; as=fetch"
  });
  
  const [user, settings] = await Promise.all([
    fetchUser(),
    fetchSettings()
  ]);
  
  return { user, settings };
});

Response Utilities

Custom Response Headers

Set custom headers and status codes on responses.

// Example patterns for custom responses
const customHeadersHandler = defineHandler((event) => {
  // Set custom headers using event.res
  event.res.setHeader("X-API-Version", "1.0");
  event.res.setHeader("Cache-Control", "max-age=3600");
  event.res.setStatus(201);
  
  return { created: true };
});

Usage Examples:

// CORS headers
const corsHandler = defineHandler((event) => {
  event.res.setHeader("Access-Control-Allow-Origin", "*");
  event.res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
  event.res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
  
  return { message: "CORS enabled" };
});

// Cache control
const cachedHandler = defineHandler((event) => {
  const isPublic = getQuery(event).public === "true";
  
  if (isPublic) {
    event.res.setHeader("Cache-Control", "public, max-age=3600");
  } else {
    event.res.setHeader("Cache-Control", "private, no-cache");
  }
  
  return { data: "cached response" };
});

// Custom status codes
const statusHandler = defineHandler((event) => {
  const { action } = getQuery(event);
  
  switch (action) {
    case "create":
      event.res.setStatus(201);
      return { created: true };
    case "accept":
      event.res.setStatus(202);
      return { accepted: true };
    default:
      event.res.setStatus(200);
      return { success: true };
  }
});

File Downloads

Send file downloads with proper headers.

// Example file download pattern
const downloadHandler = defineHandler(async (event) => {
  const filename = getRouterParam(event, "filename");
  const filePath = path.join("/uploads", filename);
  
  // Check file exists
  if (!await fs.exists(filePath)) {
    throw HTTPError.status(404, "File not found");
  }
  
  // Set download headers
  event.res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
  event.res.setHeader("Content-Type", "application/octet-stream");
  
  // Stream file
  const fileStream = fs.createReadStream(filePath);
  return new Response(fileStream);
});

Response Compression

Handle response compression and encoding.

// Example compression pattern
const compressedHandler = defineHandler((event) => {
  const acceptEncoding = event.req.headers.get("accept-encoding") || "";
  
  if (acceptEncoding.includes("gzip")) {
    event.res.setHeader("Content-Encoding", "gzip");
  }
  
  const largeData = generateLargeDataset();
  return { data: largeData };
});

Response Types

Response Configuration Types

/**
 * Response configuration options
 */
interface ResponseConfig {
  /**
   * Default status code
   */
  status?: number;
  
  /**
   * Default headers
   */
  headers?: Record<string, string>;
  
  /**
   * Serialization options
   */
  serializer?: (value: any) => string | Uint8Array;
}

/**
 * Iteration source type for streaming
 */
type IterationSource<Value, Return = any> = 
  | Iterable<Value>
  | AsyncIterable<Value>
  | Generator<Value, Return>
  | AsyncGenerator<Value, Return>;

Advanced Response Patterns

Response Transformers

Transform responses based on content type or other criteria.

// Example response transformer
const transformerHandler = defineHandler(async (event) => {
  const accept = event.req.headers.get("accept");
  const data = { message: "Hello", timestamp: Date.now() };
  
  if (accept?.includes("application/xml")) {
    event.res.setHeader("Content-Type", "application/xml");
    return `<response><message>${data.message}</message><timestamp>${data.timestamp}</timestamp></response>`;
  }
  
  return data; // Default JSON
});

Conditional Responses

Send different responses based on conditions.

// Example conditional response
const conditionalHandler = defineHandler((event) => {
  const userAgent = event.req.headers.get("user-agent") || "";
  const isMobile = /Mobile|Android|iPhone/i.test(userAgent);
  
  if (isMobile) {
    return html(event, `
      <html>
        <meta name="viewport" content="width=device-width">
        <body><h1>Mobile Version</h1></body>
      </html>
    `);
  }
  
  return html(event, `
    <html>
      <body><h1>Desktop Version</h1></body>
    </html>
  `);
});