Sending Response

H3 automatically converts any returned value into a web response.

Values returned from Event Handlers are automatically converted to a web Response by H3.

Example: Simple event handler function.

const handler = defineHandler((event) => ({ hello: "world" }));

H3 smartly converts handler into:

const handler = (event) =>
  new Response(JSON.stringify({ hello: "world" }), {
    headers: {
      "content-type": "application/json;charset=UTF-8",
    },
  });
🚀 H3 uses srvx FastResponse internally to optimize performances in Node.js runtime.

If the returned value from event handler is a Promise or from an async function, H3 will wait for it to resolve before sending the response.

If an error is thrown, H3 automatically handles it with error handler.

Read more in Error Handling.

Preparing Response

Before returning a response in main handler, you can prepare response headers and status using event.res.

defineHandler((event) => {
  event.res.status = 200;
  event.res.statusText = "OK";
  event.res.headers.set("Content-Type", "text/html");
  return "<h1>Hello, World</h1>";
});
If a full Response value is returned, prepared status is discarded and headers will be merged/overriden. For performance reasons, it is best to only set headers only from final Response in this case.
If an Error happens, prepared status and headers will be discarded.

Response Types

H3 smartly converts JavaScript values into web Response.

JSON Serializable Value

Returning a JSON serializable value (object, array, number or boolean), it will be stringified using JSON.stringiy() and sent with default application/json content-type.

Example:

app.get("/", (event) => ({ hello: "world" }));
Returned objects with .toJSON() property can customize serialization behavior. Check MDN docs for more info.

String

Returning a string value, sends it as plain text body.

If not setting content-type header, it can default to text/plain;charset=UTF-8.

Example: Send HTML response.

app.get("/", (event) => {
  event.res.headers.set("Content-Type", "text/html;charset=UTF-8");
  return "<h1>hello world</h1>";
});

You can also use html utility as shortcut.

import { html } from "h3";

app.get("/", (event) => html(event, "<h1>hello world</h1>"));

Response

Returning a web Response, sends-it as final reponse.

Example:

app.get(
  "/",
  (event) =>
    new Response("Hello, world!", { headers: { "x-powered-by": "H3" } }),
);
When sending a Response, any prepared headers that set before, will be merged as default headers. event.res.{status,statusText} will be ignored. For performance reasons, it is best to only set headers only from final Response.

ReadableStream or Readable

Returning a ReadableStream or Node.js Readable sends it as stream.

ArrayBuffer or Uint8Array or Buffer

Send binary ArrayBuffer, Uint8Array or node Buffer.

content-length header will be automatically set.

Blob

Send a Blob as stream.

Content-type and Content-Length headers will be automatically set.

File

Send a File as stream.

Content-type, Content-Length and Content-Disposition headers will be automatically set.

Special Types

Some less commonly possible values for response types.

null or undefined

Sends a response with empty body.

If there is no return statement in event handler, it is same as return undefined.

Error

Retuning an Error instance will send it.

It is better to throw errors instead of returning them. This allows proper propagation from any nested utility.
Read more in Error Handling.

BigInt

Value will be sent as stringified version of BigInt number.

Returning a JSON object, does not allows BigInt serialization. You need to implement .toJSON. Check MDN docs for more info.

Symbol or Function

Returning Symbol or Function has undetermined behavior. Currently, H3 sends a string-like representation of unknown Symbols and Functions but this behavior might be changed to throw an error in the future versions.

There are some internal known Symbols H3 internally uses:

  • Symbol.for("h3.notFound"): Indicate no route is found to throw a 404 error.
  • Symbol.for("h3.handled"): Indicate request is somehow handled and H3 should not continue (Node.js specific).