Migration guide for v1 to v2
H3 version 2 includes some behavior and API changes that you need to consider applying when migrating.
Latest Node.js and ESM-only
If your application is currently using CommonJS modules (require and module.exports), You can still use require("h3") thanks to require(esm) supported in latest Node.js versions.
You can alternatively use other compatible runtimes Bun or Deno.
Web Standards
When using Node.js, H3 uses a compatibility layer (💥 srvx) and in other runtimes uses native web compatibility APIs.
Access to the native event.node.{req,res} is only available when running server in Node.js runtime.
event.web is renamed to event.req (instance of web Request).
Response Handling
If you were previously using methods below, you can replace them with return statements returning a text, JSON, stream, or web Response (h3 smartly detects and handles each):
send(event, value): Migrate toreturn <value>.sendError(event, <error>): Migrate tothrow createError(<error>).sendStream(event, <stream>): Migrate toreturn <stream>.sendWebResponse(event, <response>): Migrate toreturn <response>.
Other send utils that are renamed and need explicit return:
sendNoContent(event)/return null: Migrate toreturn noContent(event).sendIterable(event, <value>): Migrate toreturn iterable(event, <value>).sendProxy(event, target): Migrate toreturn proxy(event, target).handleCors(event): Check return value and earlyreturnif handled(notfalse).serveStatic(event, content): Make sure to addreturnbefore.sendRedirect(event, location, code): Migrate toreturn redirect(event, location, code).
H3 and Router
Instead of
createApp() and createRouter() you can use new H3().Any handler can return a response. If middleware don't return a response, next handlers will be tried and finally make a 404 if neither responses. Router handlers can return or not return any response, in this case, H3 will send a simple 200 with empty content.
H3 migrated to a brand new route-matching engine (🌳 rou3). You might experience slight (but more intuitive) behavior changes for matching patterns.
Other changes from v1:
- Middleware added with
app.use("/path", handler)only matches/path(not/path/foo/bar). For matching all subpaths like before, it should be updated toapp.use("/path/**", handler). - The
event.pathreceived in each handler will have a full path without omitting the prefixes. usewithBase(base, handler)utility to make prefixed app. (example:withBase("/api", app.handler)). router.add(path, method: Method | Method[]signature is changed torouter.add(method: Method, path)router.use(path, handler)is deprecated. Userouter.all(path, handler)instead.app.use(() => handler, { lazy: true })is no supported anymore. Instead you can useapp.use(defineLazyEventHandler(() => handler), { lazy: true }).app.use(["/path1", "/path2"], ...)andapp.use("/path", [handler1, handler2])are not supported anymore. Instead, use multipleapp.use()calls.app.resolve(path)removed.
Request Body
event.req.* methods which is based on web Request interface.readBody(event) utility will use JSON.parse or URLSearchParams for parsing requests with application/x-www-form-urlencoded content-type.
- For text: Use event.req.text().
- For json: Use event.req.json().
- For formData: Use event.req.formData().
- For stream: Use event.req.body.
Behavior changes:
- Body utils won't throw an error if the incoming request has no body (or is a
GETmethod for example) but instead, return empty values. - Native
request.jsonandreadBodydoes not use unjs/destr anymore. You should always filter and sanitize data coming from user to avoid prototype-poisoning.
Cookie and Headers
Headers for all utils.Header values are always a plain string now (no null or undefined or number or string[]).
For the Set-Cookie header, you can use headers.getSetCookie that always returns a string array.
Other Deprecations
H3 v2 deprecated some legacy and aliased utilities.
App and router utils
createApp/createRouter: Migrate tonew H3().
Error utils
createError/H3Error: Migrate toHTTPErrorisError: Migrate toHTTPError.isError
Handler utils
eventHandler/defineEventHandler: Migrate todefineHandler(you can also directly use a function!).lazyEventHandler: Migrate todefineLazyEventHandler.isEventHandler: (removed) Any function can be an event handler.useBase: Migrate towithBase.defineRequestMiddlewareanddefineResponseMiddlewareremoved.
Request utils
getHeader/getRequestHeader: Migrate toevent.req.headers.get(name).getHeaders/getRequestHeaders: Migrate toObject.fromEntries(event.req.headers.entries()).getRequestPath: Migrate toevent.pathorevent.url.getMethod: Migrate toevent.method.
Response utils
getResponseHeader/getResponseHeaders: Migrate toevent.res.headers.get(name)setHeader/setResponseHeader/setHeaders/setResponseHeaders: Migrate toevent.res.headers.set(name, value).appendHeader/appendResponseHeader/appendResponseHeaders: Migrate toevent.res.headers.append(name, value).removeResponseHeader/clearResponseHeaders: Migrate toevent.res.headers.delete(name)appendHeaders: Migrate toappendResponseHeaders.defaultContentType: Migrate toevent.res.headers.set("content-type", type)getResponseStatus/getResponseStatusText/setResponseStatus: Useevent.res.statusandevent.res.statusText.
Node.js utils
defineNodeListener: Migrate todefineNodeHandler.fromNodeMiddleware: Migrate tofromNodeHandler.toNodeListener: Migrate totoNodeHandler.createEvent: (removed): Use Node.js adapter (toNodeHandler(app)).fromNodeRequest: (removed): Use Node.js adapter (toNodeHandler(app)).promisifyNodeListener(removed).callNodeListener: (removed).
Web Utils
fromPlainHandler: (removed) Migrate to Web API.toPlainHandler: (removed) Migrate to Web API.fromPlainRequest(removed) Migrate to Web API or usemockEventutil for testing.callWithPlainRequest(removed) Migrate to Web API.fromWebRequest: (removed) Migrate to Web API.callWithWebRequest: (removed).
Body Utils
readRawBody: Migrate toevent.req.text()orevent.req.arrayBuffer().getBodyStream/getRequestWebStream: Migrate toevent.req.body.readFormData/readMultipartFormData/readFormDataBody: Migrate toevent.req.formData().
Other Utils
isStream: Migrate toinstanceof ReadableStream.isWebResponse: Migrate toinstanceof Response.splitCookiesString: UsesplitSetCookieStringfrom cookie-es.MIMES: (removed).
Type Exports
App: Migrate toH3.AppOptions: Migrate toH3Config._RequestMiddleware: Migrate toRequestMiddleware._ResponseMiddleware: Migrate toResponseMiddleware.NodeListener: Migrate toNodeHandler.TypedHeaders: Migrate toRequestHeadersandResponseHeaders.HTTPHeaderName: Migrate toRequestHeaderNameandResponseHeaderName.H3Headers: Migrate to nativeHeaders.H3Response: Migrate to nativeResponse.MultiPartData: Migrate to nativeFormData.RouteNode: Migrate toRouterEntry.CreateRouterOptions: Migrate toRouterOptions.
Removed type exports: WebEventContext, NodeEventContext, NodePromisifiedHandler, AppUse, Stack, InputLayer, InputStack, Layer, Matcher, PlainHandler, PlainRequest, PlainResponse, WebHandler.