Response utilities for sending different content types, redirects, status codes, streaming responses, and converting values to standard Response objects.
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;
});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
});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);
});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());
});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 };
});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 };
}
});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);
});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 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>;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
});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>
`);
});