Event Handler
After creating an app instance, you can start defining your application logic using event handlers.
An event handler is a function that receives an Event
instance and returns a response. You can compare it to controllers in other frameworks.
Defining event handlers
You can define event handlers using defineEventHandler
or eventHandler
utilities:
defineEventHandler
and eventHandler
interchangeably. They are aliases. You can use the one you prefer but stick to it for consistency.import { defineEventHandler } from "h3";
defineEventHandler((event) => {
return "Response";
});
The callback function can be sync or async:
defineEventHandler(async (event) => {
return "Response";
});
Object Syntax
You can use an object syntax in defineEventHandler
for more flexible options.
defineEventHandler({
onRequest: [],
onBeforeResponse: []
handler: (event) => {
return "Response";
},
})
Responses Types
Values returned from event handlers are automatically converted to responses. It can be:
- JSON serializable value. If returning a JSON object or serializable value, it will be stringified and sent with default
application/json
content-type. string
: Sent as-is using defaulttext/html
content-type.null
: h3 with end response with204 - No Content
status code.- Web
ReadableStream
or nodeReadable
- Web
ArrayBuffer
or nodeBuffer
- Web Fetch Response
Error
instance. It's supported but recommended to throw errors instead of returning them usingcreateError
utility.
Any of above values could also be wrapped in a Promise
. This means that you can return a Promise
from your event handler and h3 will wait for it to resolve before sending the response.
Example: Send HTML response:
app.use(defineEventHandler(async (event) => "<h1>Hello world!</h1>"));
Example: Send JSON response:
app.use(
"/api",
defineEventHandler(async (event) => ({ url: event.node.req.url })),
);
Example: Send a promise:
app.use(
defineEventHandler(async (event) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ url: event.node.req.url });
}, 1000);
});
}),
);
Error Handling
You can easily control the error returned by using the createError
utility.
import { createError, defineEventHandler } from "h3";
app.use(
"/validate",
defineEventHandler((event) => {
throw createError({
status: 400,
statusMessage: "Bad Request",
message: "Invalid user input",
data: { field: "email" }
});
}),
);
This will end the request with 400 - Bad Request
status code and the following JSON response:
{
"status": 400,
"message": "An error occurred"
}
String vs. Object Errors
When creating an error using createError
, you also have the option to pass a string instead of an object. Doing so will set the message
property of the error. In this case, the statusCode
will default to 500
.
import { createError, defineEventHandler } from "h3";
app.use(
"/hello",
defineEventHandler((event) => {
throw createError("An error occurred");
}),
);
message
contains a brief, human-readable description of the error, while statusMessage
is specific to HTTP responses and describes the status text related to the response status code.
In a client-server context, using a short statusMessage
is recommended because it can be accessed on the client side. Otherwise, a message
passed to createError
on the server will not propagate to the client (you can use data
instead). Consider avoiding to put dynamic user input to the message to avoid potential security issues.Internal errors
If during calling an event handler an error with new Error()
will be thrown (without createError
), h3 will automatically catch as a 500 - Internal Server Error
status response considering it an unhandled error.
app.use(
"/hello",
defineEventHandler((event) => {
// Do NOT do this and use createError()!
throw new Error("Something went wrong");
}),
);
Lazy event handlers
You can define lazy event handlers using defineLazyEventHandler
or lazyEventHandler
utilities. This allow you to define some one-time logic that will be executed only once when the first request matching the route is received.
A lazy event handler must return an event handler:
import { defineLazyEventHandler, defineEventHandler } from "h3";
app.use(
defineLazyEventHandler(() => {
console.log("This will be executed only once");
// This will be executed only once
return defineEventHandler((event) => {
// This will be executed on every request
return "Response";
});
}),
);
This is useful to define some one-time logic such as configuration, class initialization, heavy computation, etc.
Middleware
Event handlers that don't return any value act as middleware. They can be used to add side effects to your application such as logging, caching, etc or to modify the request or response.
Similar to normal event handlers, you can define middleware using defineEventHandler
or eventHandler
utilities:
defineEventHandler((event) => {
console.log(`Middleware. Path: ${event.path}`);
});
event
.
If you return a response, it will act as a normal event handler!Registering middleware
Then, you can register middleware to app instance using the use
method:
app.use(
defineEventHandler((event) => {
console.log("Middleware 1");
}),
);
app.use(
defineEventHandler((event) => {
console.log("Middleware 2");
}),
);
app.use(
defineEventHandler((event) => {
return "Response";
}),
);
You can define as much middleware as you need. They will be called in order of registration.
Converting to h3 handler
There are situations that you might want to convert an event handler or utility made for Node.js or another framework to h3. There are built-in utils to do this.
Converting from Node.js handlers
If you have a legacy request handler with (req, res) => {}
syntax made for Node.js, you can use fromNodeListener
to convert it to an h3 event handler.
import { createApp, fromNodeListener } from "h3";
import exampleMiddleware from "example-node-middleware";
export const app = createApp();
app.use(fromNodeListener(exampleMiddleware()));
Converting from Web handlers
You can convert a fetch-like function (with Request => Response
signuture) into an h3 event handler using fromWebHandler
util.
import { webHandler } from "web-handler"; // This package doesn't exist, it's just an example
import { createApp, fromWebHandler } from "h3";
export const app = createApp();
app.use(fromWebHandler(webHandler));