| icon | icons8:up-round |
|---|---|
| title | Migration |
H3 version 2 includes some behavior and API changes that you need to consider applying when migrating.
Note
Currently H3 v2 in beta stage. You can try with nightly channel.
Note
This is an undergoing migration guide and might be updated.
Tip
H3 has a brand new documentation rewrite. Head to the new Guide section to learn more!
Tip
H3 v2 requires Node.js >= 20.11 (latest LTS recommended) .
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.
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).
Tip
You should always explicitly return the response body or throw an error.
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().sendIterable(event, <value>): Migrate toreturn iterable(<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(location, code).
:read-more{to="/guide/basics/response" title="Sending Response"}
Tip
Router function is now integrated into the H3 core.
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.
:read-more{to="/guide/basics/lifecycle" title="Request Lifecycle"}
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.
:read-more{to="/guide/basics/routing" title="Routing"}
:read-more{to="/guide/basics/middleware" title="Middleware"}
Tip
Most of request body utilities can now be replaced with native 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.
Tip
H3 now natively uses standard web 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.
H3 v2 deprecated some legacy and aliased utilities.
createApp/createRouter: Migrate tonew H3().
createError/H3Error: Migrate toHTTPErrorisError: Migrate toHTTPError.isError
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.
getHeader/getRequestHeader: Migrate toevent.req.headers.get(name).getHeaders/getRequestHeaders: Migrate toObject.fromEntries(event.req.headers.entries()).getRequestPath: Migrate toevent.url.pathname.getMethod: Migrate toevent.req.method.
Note
The following H3Event properties are deprecated in v2 and might be removed in a future version:
event.path→ useevent.url.pathname + event.url.searchevent.method→ useevent.req.methodevent.headers→ useevent.req.headersevent.node→ useevent.runtime.node
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.
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).
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).
readRawBody: Migrate toevent.req.text()orevent.req.arrayBuffer().getBodyStream/getRequestWebStream: Migrate toevent.req.body.readFormData/readMultipartFormData/readFormDataBody: Migrate toevent.req.formData().
isStream: Migrate toinstanceof ReadableStream.isWebResponse: Migrate toinstanceof Response.splitCookiesString: UsesplitSetCookieStringfrom cookie-es.MIMES: (removed).
Note
There might be more type changes.
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.