Repository: unjs/h3
Pull request #1006 (2025-04-01 16:58:38)

refactor: cleanup event interface
Cleanup `event` interface to prepare v2 RC.

```ts
interface H3Event {
  readonly context: H3EventContext;
  readonly req: Request; // with srvx addons `req.{bun,node,deno,...}`
  readonly url: URL; // fast!

  readonly res: {
    status?: number;
    statusText?: string;
    readonly headers: Headers;
  };

  // deprecated
  node?: ServerRequest["node"];
  readonly path: string;
  readonly method: string;
  readonly headers: Headers;
}
```

### Changes from [v1 event](https://github.com/unjs/h3/blob/v1/src/event/event.ts):

- Deprecated: 
  - `event.path` => `event.url`
  - `event.method` => `event.req.method`
  - `event.headers` => `event.req.headers`
  - `event.node` => `event.req.{node,bun,deno,...}` (srvx)
- Removed: `event.web`, `event.handled`
- Replaced: `event.{req,res}` (deperecated in v1) are now **Web interfaces**.


### Changes from current v2 nightly:

- `event.request` renamed to `event.req`
- `event.response` renamed to `event.res`
  - `event.response.setHeader` => `event.res.headers.set`
- `event.{pathname,query,queryString}` moved to `event.url`
  - Now `event.url` is a `FastURL` that allows using standard `URL` interface + still being fast!
- `event.ip` moved to `event.req.remoteAddress` (srvx)
- Using `event.url` for fingerprint util and 404 error message

## Benchmarks

With the migration of `event.url` to the new `FastURL`, access to the `pathname` (for route matching) is still fast, yet we don't need extra event props and since interface is standard, we can revert back to `globalThis.URL` as soon runtimes got faster.

With the removal of the fast path for `event.response`, it seems we are actually a little faster. (Thanks to runtime improvements for `Headers` ❤️)

```
clk: ~4.37 GHz
cpu: Apple M4 Pro
runtime: bun 1.2.8 (arm64-darwin)

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
_dist                         16.97 µs/iter  16.42 µs  53.13 µs █▄▂▁▁▁▁▁▁▁▁
_nightly                      19.46 µs/iter  19.88 µs  40.92 µs ▆█▃▂▁▁▁▁▁▁▁

summary
  _dist
   1.15x faster than _nightly
```

(Node.js is slower now, pending migration to srvx fast-paths for Node adapter)
 
### FastURL

Normal access to `event.url` (`event.url.pathname` used by router) is WAY faster in most cases, while there might be a slight penalty if slowpath is triggered (like when accessing `event.url.username` or setting any prop)

```
clk: ~4.37 GHz
cpu: Apple M4 Pro
runtime: bun 1.2.8 (arm64-darwin)

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
• pathname
------------------------------------------- -------------------------------
globalThis.URL               240.62 ns/iter 242.38 ns 271.87 ns ▂▆█▄▂▁▁▁▁▁▁
FastURL                       14.29 ns/iter  30.62 ns  38.21 ns █▁▁▁▁▁▁▁▄▁▁

summary
  FastURL
   16.84x faster than globalThis.URL

• params
------------------------------------------- -------------------------------
globalThis.URL               417.99 ns/iter 420.40 ns 451.52 ns ▂▄█▆▃▂▁▁▁▁▁
FastURL                      453.03 ns/iter 472.65 ns 511.08 ns ▂█▄▂▃▄▃▃▂▂▁

summary
  globalThis.URL
   1.08x faster than FastURL

• pathname+params
------------------------------------------- -------------------------------
globalThis.URL               462.94 ns/iter 483.77 ns 520.19 ns ▆█▃▅▆▇▅▆▂▂▁
FastURL                      200.29 ns/iter 200.74 ns 231.62 ns ▂█▃▂▁▁▁▁▁▁▁

summary
  FastURL
   2.31x faster than globalThis.URL

• pathname+params+username
------------------------------------------- -------------------------------
globalThis.URL               448.84 ns/iter 450.63 ns 476.21 ns ▁▆█▆▃▂▁▁▁▁▁
FastURL                      461.87 ns/iter 463.36 ns 501.74 ns ▂▆█▃▂▁▁▁▁▁▁

summary
  globalThis.URL
   1.03x faster than FastURL

```



Patch:
diff --git a/MIGRATION.md b/MIGRATION.md
index aa4bbe3..31029ec 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -16,7 +16,7 @@ h3 v2 includes some behavior and API changes that you need to consider applying
 
 H3 v2 is rewritten based on Web standard primitives ([`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL), [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers), [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request), and [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)).
 
-`event.node` context is only available when running in Node.js runtime and `event.web` is available via `event.request`.
+`event.node` context is only available when running in Node.js runtime and `event.web` is available via `event.req`.
 
 On Node.js runtime, h3 uses a two way proxy to sync Node.js API with Web standard API making it a seamless experience on Node.
 
@@ -75,14 +75,14 @@ Other changes from v1:
 
 ## Body utils
 
-Most of request body utilities can now be replaced with `event.request` utils which is based on standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Response) interface.
+Most of request body utilities can now be replaced with `event.req` utils which is based on standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Response) interface + platform addons from [srvx](https://srvx.unjs.io/guide/handler#additional-properties).
 
 `readBody(event)` utility will use [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) or [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) for parsing requests with `application/x-www-form-urlencoded` content-type.
 
-- For text: Use [event.request.text()](https://developer.mozilla.org/en-US/docs/Web/API/Request/text).
-- For json: Use [event.request.json()](https://developer.mozilla.org/en-US/docs/Web/API/Request/json).
-- For formData: Use [event.request.formData()](https://developer.mozilla.org/en-US/docs/Web/API/Request/formData).
-- For stream: Use [event.request.body](https://developer.mozilla.org/en-US/docs/Web/API/Request/body).
+- For text: Use [event.req.text()](https://developer.mozilla.org/en-US/docs/Web/API/Request/text).
+- For json: Use [event.req.json()](https://developer.mozilla.org/en-US/docs/Web/API/Request/json).
+- For formData: Use [event.req.formData()](https://developer.mozilla.org/en-US/docs/Web/API/Request/formData).
+- For stream: Use [event.req.body](https://developer.mozilla.org/en-US/docs/Web/API/Request/body).
 
 **Behavior changes:**
 
@@ -114,20 +114,20 @@ h3 v2 deprecated some legacy and aliased utilities.
 
 **Request:**
 
-- `getHeader` / `getRequestHeader`: Migrate to `event.request.headers.get(name)`.
-- `getHeaders` / `getRequestHeaders`: Migrate to `Object.fromEntries(event.request.headers.entries())`.
+- `getHeader` / `getRequestHeader`: Migrate to `event.req.headers.get(name)`.
+- `getHeaders` / `getRequestHeaders`: Migrate to `Object.fromEntries(event.req.headers.entries())`.
 - `getRequestPath`: Migrate to `event.path` or `event.url`.
 - `getMethod`: Migrate to `event.method`.
 
 **Response:**
 
-- `getResponseHeader` / `getResponseHeaders`: Migrate to `event.response.headers.get(name)`
-- `setHeader` / `setResponseHeader` / `setHeaders` / `setResponseHeaders`: Migrate to `event.response.headers.set(name, value)`.
-- `appendHeader` / `appendResponseHeader` / `appendResponseHeaders`: Migrate to `event.response.headers.append(name, value)`.
-- `removeResponseHeader` / `clearResponseHeaders`: Migrate to `event.response.headers.delete(name)`
+- `getResponseHeader` / `getResponseHeaders`: Migrate to `event.res.headers.get(name)`
+- `setHeader` / `setResponseHeader` / `setHeaders` / `setResponseHeaders`: Migrate to `event.res.headers.set(name, value)`.
+- `appendHeader` / `appendResponseHeader` / `appendResponseHeaders`: Migrate to `event.res.headers.append(name, value)`.
+- `removeResponseHeader` / `clearResponseHeaders`: Migrate to `event.res.headers.delete(name)`
 - `appendHeaders`: Migrate to `appendResponseHeaders`.
-- `defaultContentType`: Migrate to `event.response.headers.set("content-type", type)`
-- `getResponseStatus` / `getResponseStatusText` / `setResponseStatus`: Use `event.response.status` and `event.response.statusText`.
+- `defaultContentType`: Migrate to `event.res.headers.set("content-type", type)`
+- `getResponseStatus` / `getResponseStatusText` / `setResponseStatus`: Use `event.res.status` and `event.res.statusText`.
 
 **Node.js:**
 
@@ -150,9 +150,9 @@ h3 v2 deprecated some legacy and aliased utilities.
 
 **Body:**
 
-- `readRawBody`: Migrate to `event.request.text()` or `event.request.arrayBuffer()`.
-- `getBodyStream` / `getRequestWebStream`: Migrate to `event.request.body`.
-- `readFormData` / `readMultipartFormData` / `readFormDataBody`: Migrate to `event.request.formData()`.
+- `readRawBody`: Migrate to `event.req.text()` or `event.req.arrayBuffer()`.
+- `getBodyStream` / `getRequestWebStream`: Migrate to `event.req.body`.
+- `readFormData` / `readMultipartFormData` / `readFormDataBody`: Migrate to `event.req.formData()`.
 
 **Utils:**
 
diff --git a/docs/2.utils/1.request.md b/docs/2.utils/1.request.md
index a7116ec..c8291c6 100644
--- a/docs/2.utils/1.request.md
+++ b/docs/2.utils/1.request.md
@@ -87,7 +87,7 @@ app.use("/", (event) => {
 
 ### `getRequestURL(event, opts: { xForwardedHost?, xForwardedProto? })`
 
-Generated the full incoming request URL using `getRequestProtocol`, `getRequestHost` and `event.path`.
+Generated the full incoming request URL.
 
 If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
 
diff --git a/examples/body.ts b/examples/body.ts
index 25cfc92..578b043 100644
--- a/examples/body.ts
+++ b/examples/body.ts
@@ -6,6 +6,6 @@ app
   .get("/", () => "Try sending a POST request with a body!")
   .post("/", async (event) => {
     return {
-      body: await event.request.text(),
+      body: await event.req.text(),
     };
   });
diff --git a/examples/headers.ts b/examples/headers.ts
index 46a9dfa..286b52b 100644
--- a/examples/headers.ts
+++ b/examples/headers.ts
@@ -3,13 +3,13 @@ import { createH3 } from "h3";
 export const app = createH3();
 
 app.get("/user-agent", (event) => {
-  const userAgent = event.headers.get("user-agent");
+  const userAgent = event.req.headers.get("user-agent");
 
-  event.response.setHeader("content-type", "text/plain");
-  event.response.setHeader("x-server", "nitro");
+  event.res.headers.set("content-type", "text/plain");
+  event.res.headers.set("x-server", "nitro");
 
   return {
     userAgent: userAgent,
-    responseHeaders: Object.fromEntries(event.response.headers.entries()),
+    responseHeaders: Object.fromEntries(event.res.headers.entries()),
   };
 });
diff --git a/examples/query-params.ts b/examples/query-params.ts
index ddc8a51..140f518 100644
--- a/examples/query-params.ts
+++ b/examples/query-params.ts
@@ -3,5 +3,5 @@ import { createH3 } from "h3";
 export const app = createH3();
 
 app.get("/", (event) => {
-  return `Hello ${event.query.get("name") || "anonymous"}!`;
+  return `Hello ${event.url.searchParams.get("name") || "anonymous"}!`;
 });
diff --git a/examples/status.ts b/examples/status.ts
index 4b19cfa..36cad30 100644
--- a/examples/status.ts
+++ b/examples/status.ts
@@ -4,7 +4,7 @@ export const app = createH3();
 
 app
   .get("/not-found", (event) => {
-    event.response.status = 404;
+    event.res.status = 404;
 
     return "Not found"; // You need to explicitly return something to avoid a 404 'Cannot find any path matching "/not-found"' response.
   })
@@ -12,8 +12,8 @@ app
     const status = 400;
     const text = "Bad request message";
 
-    event.response.status = status;
-    event.response.statusText = text; // You can customize the status message.
+    event.res.status = status;
+    event.res.statusText = text; // You can customize the status message.
 
     return {
       status,
diff --git a/package.json b/package.json
index 7463423..900b9c4 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,8 @@
   "scripts": {
     "bench:bun": "bun run ./test/bench/h3.mjs",
     "bench:node": "node --expose-gc --allow-natives-syntax ./test/bench/h3.mjs",
+    "bench:url:bun": "bun run ./test/bench/url.ts",
+    "bench:url:node": "node --expose-gc --allow-natives-syntax --import jiti/register ./test/bench/url.ts",
     "build": "unbuild",
     "dev": "vitest",
     "lint": "eslint --cache . && prettier -c src test examples docs",
diff --git a/src/_deprecated.ts b/src/_deprecated.ts
index e824baa..2375bca 100644
--- a/src/_deprecated.ts
+++ b/src/_deprecated.ts
@@ -19,36 +19,36 @@ import { sanitizeStatusCode, sanitizeStatusMessage } from "./utils/sanitize";
 
 // --- Request ---
 
-/** @deprecated Please use `event.path` or `event.url` */
+/** @deprecated Please use `event.url` */
 export const getRequestPath = (event: H3Event) => event.path;
 
-/** @deprecated Please use `event.request.headers.get(name)` */
+/** @deprecated Please use `event.req.headers.get(name)` */
 export function getRequestHeader(
   event: H3Event,
   name: string,
 ): string | undefined {
-  return event.request.headers.get(name) || undefined;
+  return event.req.headers.get(name) || undefined;
 }
 
-/** @deprecated Please use `event.request.headers.get(name)` */
+/** @deprecated Please use `event.req.headers.get(name)` */
 export const getHeader = getRequestHeader;
 
-/** @deprecated Please use `Object.fromEntries(event.request.headers.entries())` */
+/** @deprecated Please use `Object.fromEntries(event.req.headers.entries())` */
 export function getRequestHeaders(event: H3Event): Record<string, string> {
-  return Object.fromEntries(event.request.headers.entries());
+  return Object.fromEntries(event.req.headers.entries());
 }
 
-/** @deprecated Please use `Object.fromEntries(event.request.headers.entries())` */
+/** @deprecated Please use `Object.fromEntries(event.req.headers.entries())` */
 export const getHeaders = getRequestHeaders;
 
-/** @deprecated Please use `event.request.method` */
+/** @deprecated Please use `event.req.method` */
 export function getMethod(event: H3Event, defaultMethod = "GET") {
-  return (event.request.method || defaultMethod).toUpperCase();
+  return (event.req.method || defaultMethod).toUpperCase();
 }
 
 // --- Request Body ---
 
-/** @deprecated Please use `event.request.text()` or `event.request.arrayBuffer()` */
+/** @deprecated Please use `event.req.text()` or `event.req.arrayBuffer()` */
 export function readRawBody<E extends "utf8" | false = "utf8">(
   event: H3Event,
   encoding = "utf8" as E,
@@ -56,26 +56,26 @@ export function readRawBody<E extends "utf8" | false = "utf8">(
   ? Promise<Uint8Array | undefined>
   : Promise<string | undefined> {
   return encoding
-    ? (event.request.text() as any)
-    : (event.request.arrayBuffer().then((r) => new Uint8Array(r)) as any);
+    ? (event.req.text() as any)
+    : (event.req.arrayBuffer().then((r) => new Uint8Array(r)) as any);
 }
 
-/** @deprecated Please use `event.request.formData()` */
+/** @deprecated Please use `event.req.formData()` */
 export async function readFormDataBody(event: H3Event): Promise<FormData> {
-  return event.request.formData();
+  return event.req.formData();
 }
 
-/** @deprecated Please use `event.request.formData()` */
+/** @deprecated Please use `event.req.formData()` */
 export const readFormData = readFormDataBody;
 
-/** @deprecated Please use `event.request.body` */
+/** @deprecated Please use `event.req.body` */
 export function getBodyStream(
   event: H3Event,
 ): ReadableStream<Uint8Array> | undefined {
-  return event.request.body || undefined;
+  return event.req.body || undefined;
 }
 
-/** @deprecated Please use `event.request.body` */
+/** @deprecated Please use `event.req.body` */
 export const getRequestWebStream = getBodyStream;
 
 // --- Response ---
@@ -103,12 +103,12 @@ export const sendProxy = proxy;
 /** @deprecated Please use `return iterable(event, value)` */
 export const sendIterable = iterable;
 
-/** @deprecated Please use `event.response.statusText` */
+/** @deprecated Please use `event.res.statusText` */
 export function getResponseStatusText(event: H3Event): string {
-  return event.response.statusText || "";
+  return event.res.statusText || "";
 }
 
-/** @deprecated Please use `event.response.headers.append(name, value)` */
+/** @deprecated Please use `event.res.headers.append(name, value)` */
 export function appendResponseHeader(
   event: H3Event,
   name: string,
@@ -116,100 +116,100 @@ export function appendResponseHeader(
 ): void {
   if (Array.isArray(value)) {
     for (const valueItem of value) {
-      event.response.headers.append(name, valueItem!);
+      event.res.headers.append(name, valueItem!);
     }
   } else {
-    event.response.headers.append(name, value!);
+    event.res.headers.append(name, value!);
   }
 }
 
-/** @deprecated Please use `event.response.headers.append(name, value)` */
+/** @deprecated Please use `event.res.headers.append(name, value)` */
 export const appendHeader = appendResponseHeader;
 
-/** @deprecated Please use `event.response.headers.set(name, value)` */
+/** @deprecated Please use `event.res.headers.set(name, value)` */
 export function setResponseHeader(
   event: H3Event,
   name: string,
   value: string | string[],
 ): void {
   if (Array.isArray(value)) {
-    event.response.headers.delete(name);
+    event.res.headers.delete(name);
     for (const valueItem of value) {
-      event.response.headers.append(name, valueItem!);
+      event.res.headers.append(name, valueItem!);
     }
   } else {
-    event.response.headers.set(name, value!);
+    event.res.headers.set(name, value!);
   }
 }
 
-/** @deprecated Please use `event.response.headers.set(name, value)` */
+/** @deprecated Please use `event.res.headers.set(name, value)` */
 export const setHeader = setResponseHeader;
 
-/** @deprecated Please use `event.response.headers.set(name, value)` */
+/** @deprecated Please use `event.res.headers.set(name, value)` */
 export function setResponseHeaders(
   event: H3Event,
   headers: ResponseHeaders,
 ): void {
   for (const [name, value] of Object.entries(headers)) {
-    event.response.headers.set(name, value!);
+    event.res.headers.set(name, value!);
   }
 }
 
-/** @deprecated Please use `event.response.headers.set(name, value)` */
+/** @deprecated Please use `event.res.headers.set(name, value)` */
 export const setHeaders = setResponseHeaders;
 
-/** @deprecated Please use `event.response.status` */
+/** @deprecated Please use `event.res.status` */
 export function getResponseStatus(event: H3Event): number {
-  return event.response.status || 200;
+  return event.res.status || 200;
 }
 
-/** @deprecated Please directly set `event.response.status` and `event.response.statusText` */
+/** @deprecated Please directly set `event.res.status` and `event.res.statusText` */
 export function setResponseStatus(
   event: H3Event,
   code?: number,
   text?: string,
 ): void {
   if (code) {
-    event.response.status = sanitizeStatusCode(code, event.response.status);
+    event.res.status = sanitizeStatusCode(code, event.res.status);
   }
   if (text) {
-    event.response.statusText = sanitizeStatusMessage(text);
+    event.res.statusText = sanitizeStatusMessage(text);
   }
 }
 
-/** @deprecated Please use `event.response.headers.set("content-type", type)` */
+/** @deprecated Please use `event.res.headers.set("content-type", type)` */
 export function defaultContentType(event: H3Event, type?: string) {
   if (
     type &&
-    event.response.status !== 304 /* unjs/h3#603 */ &&
-    !event.response.headers.has("content-type")
+    event.res.status !== 304 /* unjs/h3#603 */ &&
+    !event.res.headers.has("content-type")
   ) {
-    event.response.headers.set("content-type", type);
+    event.res.headers.set("content-type", type);
   }
 }
 
-/** @deprecated Please use `Object.fromEntries(event.response.headers.entries())` */
+/** @deprecated Please use `Object.fromEntries(event.res.headers.entries())` */
 export function getResponseHeaders(event: H3Event): Record<string, string> {
-  return Object.fromEntries(event.response.headers.entries());
+  return Object.fromEntries(event.res.headers.entries());
 }
 
-/** @deprecated Please use `event.response.headers.get(name)` */
+/** @deprecated Please use `event.res.headers.get(name)` */
 export function getResponseHeader(
   event: H3Event,
   name: string,
 ): string | undefined {
-  return event.response.headers.get(name) || undefined;
+  return event.res.headers.get(name) || undefined;
 }
 
-/** @deprecated Please use `event.response.headers.delete(name)` instead. */
+/** @deprecated Please use `event.res.headers.delete(name)` instead. */
 export function removeResponseHeader(
   event: H3Event,
   name: ResponseHeaderName,
 ): void {
-  return event.response.headers.delete(name);
+  return event.res.headers.delete(name);
 }
 
-/** @deprecated Please use `event.response.headers.append(name, value)` */
+/** @deprecated Please use `event.res.headers.append(name, value)` */
 export function appendResponseHeaders(
   event: H3Event,
   headers: ResponseHeaders,
@@ -219,21 +219,21 @@ export function appendResponseHeaders(
   }
 }
 
-/** @deprecated Please use `event.response.headers.append(name, value)` */
+/** @deprecated Please use `event.res.headers.append(name, value)` */
 export const appendHeaders = appendResponseHeaders;
 
-/** @deprecated Please use `event.response.headers.delete` */
+/** @deprecated Please use `event.res.headers.delete` */
 export function clearResponseHeaders(
   event: H3Event,
   headerNames?: ResponseHeaderName[],
 ): void {
   if (headerNames && headerNames.length > 0) {
     for (const name of headerNames) {
-      event.response.headers.delete(name);
+      event.res.headers.delete(name);
     }
   } else {
-    for (const name of event.response.headers.keys()) {
-      event.response.headers.delete(name);
+    for (const name of event.res.headers.keys()) {
+      event.res.headers.delete(name);
     }
   }
 }
diff --git a/src/adapters.ts b/src/adapters.ts
index 3fdc48a..2524a46 100644
--- a/src/adapters.ts
+++ b/src/adapters.ts
@@ -21,7 +21,7 @@ export function toWebHandler(
 export function fromWebHandler(
   handler: (request: Request, context?: H3EventContext) => Promise<Response>,
 ): EventHandler {
-  return (event) => handler(event.request, event.context);
+  return (event) => handler(event.req, event.context);
 }
 
 /**
diff --git a/src/event.ts b/src/event.ts
index 56d18e2..b778702 100644
--- a/src/event.ts
+++ b/src/event.ts
@@ -1,114 +1,48 @@
 import type { ServerRequest } from "srvx/types";
-import type { H3Event, H3EventContext, HTTPMethod } from "./types";
-import type { H3EventResponse } from "./types/event";
+import type { H3Event, H3EventContext } from "./types";
 
-const H3EventContext = /* @__PURE__ */ (() => {
-  const C = function () {};
-  C.prototype = Object.create(null);
-  return C;
-})() as unknown as { new (): H3EventContext };
+import { EmptyObject } from "./utils/internal/obj";
+import { FastURL } from "./url";
 
-const HeadersObject = /* @__PURE__ */ (() => {
-  const C = function () {};
-  C.prototype = Object.create(null);
-  return C;
-})() as unknown as { new (): H3EventContext };
-
-export class H3WebEvent implements H3Event {
+export class _H3Event implements H3Event {
   static __is_event__ = true;
-  context: H3EventContext;
-  request: ServerRequest;
-  response: H3EventResponse;
-
-  _url?: URL;
-  _pathname?: string;
-  _urlqindex?: number;
-  _query?: URLSearchParams;
-  _queryString?: string;
 
-  constructor(request: ServerRequest, context?: H3EventContext) {
-    this.context = context || new H3EventContext();
-    this.request = request;
-    this.response = new WebEventResponse();
-  }
-
-  get method(): HTTPMethod {
-    return this.request.method as HTTPMethod;
-  }
+  req: ServerRequest;
+  url: URL;
+  context: H3EventContext;
+  _res?: H3EventResponse;
 
-  get headers(): Headers {
-    return this.request.headers;
+  constructor(req: ServerRequest, context?: H3EventContext) {
+    this.context = context || new EmptyObject();
+    this.req = req;
+    this.url = new FastURL(req.url);
   }
 
-  get url() {
-    if (!this._url) {
-      this._url = new URL(this.request.url);
+  get res() {
+    if (!this._res) {
+      this._res = new H3EventResponse();
     }
-    return this._url;
+    return this._res;
   }
 
   get path() {
-    return this.pathname + this.queryString;
-  }
-
-  get pathname() {
-    if (this._url) {
-      return this._url.pathname; // reuse parsed URL
-    }
-    if (!this._pathname) {
-      const url = this.request.url;
-      const protoIndex = url.indexOf("://");
-      if (protoIndex === -1) {
-        return this.url.pathname; // deoptimize
-      }
-      const pIndex = url.indexOf("/", protoIndex + 4 /* :// */);
-      if (pIndex === -1) {
-        return this.url.pathname; // deoptimize
-      }
-      const qIndex = (this._urlqindex = url.indexOf("?", pIndex));
-      this._pathname = url.slice(pIndex, qIndex === -1 ? undefined : qIndex);
-    }
-    return this._pathname;
+    return this.url.pathname + this.url.search;
   }
 
-  get query() {
-    if (this._url) {
-      return this._url.searchParams; // reuse parsed URL
-    }
-    if (!this._query) {
-      this._query = new URLSearchParams(this.queryString);
-    }
-    return this._query;
+  get method() {
+    return this.req.method;
   }
 
-  get queryString() {
-    if (this._url) {
-      return this._url.search; // reuse parsed URL
-    }
-    if (!this._queryString) {
-      const qIndex = this._urlqindex;
-      if (qIndex === -1) {
-        this._queryString = "";
-      } else {
-        this._queryString =
-          this._urlqindex === undefined
-            ? this.url.search // deoptimize (mostly unlikely as pathname accessor is always used)
-            : this.request.url.slice(this._urlqindex);
-      }
-    }
-    return this._queryString;
+  get headers() {
+    return this.req.headers;
   }
 
   get node() {
-    return this.request.node;
-  }
-
-  get ip() {
-    return this.request.remoteAddress;
+    return this.req.node;
   }
 
   toString(): string {
-    return `[${this.request.method}] ${this.request.url}`;
+    return `[${this.req.method}] ${this.req.url}`;
   }
 
   toJSON(): string {
@@ -116,25 +50,14 @@ export class H3WebEvent implements H3Event {
   }
 }
 
-class WebEventResponse implements H3EventResponse {
-  _headersInit?: Record<string, string>;
+class H3EventResponse {
+  status?: number;
+  // statusText?: string;
   _headers?: Headers;
-
   get headers() {
     if (!this._headers) {
-      this._headers = new Headers(this._headersInit);
+      this._headers = new Headers();
     }
     return this._headers;
   }
-
-  setHeader(name: string, value: string): void {
-    if (this._headers) {
-      this._headers.set(name, value);
-    } else {
-      if (!this._headersInit) {
-        this._headersInit = new HeadersObject();
-      }
-      this._headersInit[name] = value;
-    }
-  }
 }
diff --git a/src/h3.ts b/src/h3.ts
index c1d8c50..18b2afd 100644
--- a/src/h3.ts
+++ b/src/h3.ts
@@ -18,7 +18,7 @@ import {
 } from "rou3";
 import { serve as srvxServe, type ServerOptions } from "srvx";
 import { getPathname, joinURL } from "./utils/internal/path";
-import { H3WebEvent } from "./event";
+import { _H3Event } from "./event";
 import { kNotFound, prepareResponse } from "./response";
 import { createError } from "./error";
 
@@ -95,7 +95,7 @@ class _H3 implements H3 {
     }
 
     // Create a new event instance
-    const event = new H3WebEvent(request, context);
+    const event = new _H3Event(request, context);
 
     // Execute the handler
     let handlerRes: unknown | Promise<unknown>;
@@ -135,7 +135,7 @@ class _H3 implements H3 {
   }
 
   _handler(event: H3Event) {
-    const pathname = event.pathname;
+    const pathname = event.url.pathname;
 
     let _chain: Promise<unknown> | undefined;
 
@@ -153,7 +153,7 @@ class _H3 implements H3 {
           if (_previous !== undefined && _previous !== kNotFound) {
             return _previous;
           }
-          if (m.method && m.method !== event.request.method) {
+          if (m.method && m.method !== event.req.method) {
             return;
           }
           return m.handler(event);
@@ -164,7 +164,7 @@ class _H3 implements H3 {
     // 3. Middleware router
     const _mRouter = this._mRouter;
     if (_mRouter) {
-      const matches = findAllRoutes(_mRouter, event.request.method, pathname);
+      const matches = findAllRoutes(_mRouter, event.req.method, pathname);
       if (matches.length > 0) {
         _chain = _chain || Promise.resolve();
         for (const match of matches) {
@@ -182,7 +182,7 @@ class _H3 implements H3 {
 
     // 4. Route handler
     if (this._router) {
-      const match = findRoute(this._router, event.request.method, pathname);
+      const match = findRoute(this._router, event.req.method, pathname);
       if (match) {
         if (_chain) {
           return _chain.then((_previous) => {
diff --git a/src/response.ts b/src/response.ts
index 633811a..b70ae90 100644
--- a/src/response.ts
+++ b/src/response.ts
@@ -18,13 +18,12 @@ export function prepareResponse(
 
   if (val instanceof Response) {
     // Note: preparted status and statusText are discarded in favor of response values
-    const preparedHeaders =
-      event.response._headers || event.response._headersInit;
+    const preparedHeaders = (event.res as { _headers?: Headers })._headers;
     if (!preparedHeaders) {
       return val;
     }
     // Slow path: merge headers
-    const noBody = event.method === "HEAD" || isNullStatus(val.status);
+    const noBody = event.req.method === "HEAD" || isNullStatus(val.status);
     const mergedHeaders = new Headers(preparedHeaders);
     for (const [name, value] of val.headers) {
       if (name === "set-cookie") {
@@ -42,12 +41,12 @@ export function prepareResponse(
 
   // We always prepare response body to resolve status and headers
   const body = prepareResponseBody(val, event, config);
-  const status = event.response.status;
+  const status = event.res.status;
   const responseInit: PreparedResponse = {
-    body: event.method === "HEAD" || isNullStatus(status) ? null : body,
+    body: event.req.method === "HEAD" || isNullStatus(status) ? null : body,
     status,
-    statusText: event.response.statusText,
-    headers: event.response._headers || event.response._headersInit,
+    statusText: event.res.statusText,
+    headers: (event.res as { _headers?: Headers })._headers,
   };
 
   return new SrvxResponse(responseInit.body, responseInit) as Response;
@@ -80,7 +79,7 @@ export function prepareResponseBody(
     return prepareErrorResponseBody(
       {
         statusCode: 404,
-        statusMessage: `Cannot find any route matching [${event.request.method}] ${event.path}`,
+        statusMessage: `Cannot find any route matching [${event.req.method}] ${event.url}`,
       },
       event,
       config,
@@ -96,7 +95,7 @@ export function prepareResponseBody(
 
   // Buffer (should be before JSON)
   if (val instanceof Uint8Array) {
-    event.response.setHeader("content-length", val.byteLength.toString());
+    event.res.headers.set("content-length", val.byteLength.toString());
     return val;
   }
 
@@ -107,30 +106,30 @@ export function prepareResponseBody(
 
   // JSON
   if (isJSONSerializable(val, valType)) {
-    event.response.setHeader("content-type", "application/json; charset=utf-8");
+    event.res.headers.set("content-type", "application/json; charset=utf-8");
     return JSON.stringify(val, undefined, config.debug ? 2 : undefined);
   }
 
   // BigInt
   if (valType === "bigint") {
-    event.response.setHeader("content-type", "application/json; charset=utf-8");
+    event.res.headers.set("content-type", "application/json; charset=utf-8");
     return val.toString();
   }
 
   // Web Response
   if (val instanceof Response) {
-    event.response.status = val.status;
-    event.response.statusText = val.statusText;
+    event.res.status = val.status;
+    event.res.statusText = val.statusText;
     for (const [name, value] of val.headers) {
-      event.response.setHeader(name, value);
+      event.res.headers.set(name, value);
     }
     return val.body;
   }
 
   // Blob
   if (val instanceof Blob) {
-    event.response.setHeader("content-type", val.type);
-    event.response.setHeader("content-length", val.size.toString());
+    event.res.headers.set("content-type", val.type);
+    event.res.headers.set("content-length", val.size.toString());
     return val.stream();
   }
 
@@ -155,9 +154,9 @@ export function prepareErrorResponseBody(
   config: H3Config,
 ): string {
   const error = createError(val as H3Error);
-  event.response.status = error.statusCode;
-  event.response.statusText = error.statusMessage;
-  event.response.setHeader("content-type", "application/json; charset=utf-8");
+  event.res.status = error.statusCode;
+  event.res.statusText = error.statusMessage;
+  event.res.headers.set("content-type", "application/json; charset=utf-8");
   return JSON.stringify({
     statusCode: error.statusCode,
     statusMessage: error.statusMessage,
diff --git a/src/types/event.ts b/src/types/event.ts
index 067c1c6..20eb5bd 100644
--- a/src/types/event.ts
+++ b/src/types/event.ts
@@ -1,38 +1,65 @@
-import type { EventHandlerRequest, H3EventContext, HTTPMethod } from ".";
+import type { EventHandlerRequest, H3EventContext } from ".";
 import type { ServerRequest } from "srvx/types";
 
 export interface H3Event<
   _RequestT extends EventHandlerRequest = EventHandlerRequest,
 > {
-  // Context
+  /**
+   * Event context.
+   */
   readonly context: H3EventContext;
 
-  // Platform specific
-  node?: ServerRequest["node"];
+  /**
+   * Incoming HTTP request info.
+   *
+   * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Request)
+   */
+  readonly req: ServerRequest;
 
-  // Request
-  readonly request: ServerRequest;
-  readonly method: HTTPMethod;
-  readonly path: string;
-  readonly pathname: string;
-  readonly query: URLSearchParams;
-  readonly queryString: string;
+  /**
+   * Access to the parsed request URL.
+   *
+   * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/URL)
+   */
   readonly url: URL;
-  readonly headers: Headers;
-  readonly ip?: string | undefined;
 
-  // Response
-  response: H3EventResponse;
-}
+  /**
+   * Prepared HTTP response.
+   */
+  readonly res: {
+    status?: number;
+    statusText?: string;
+    readonly headers: Headers;
+  };
 
-export interface H3EventResponse {
-  status?: number;
-  statusText?: string;
+  /**
+   * Access to the raw Node.js req/res objects.
+   *
+   * @deprecated Use `event.req.{node|deno|bun|...}.` instead.
+   */
+  node?: ServerRequest["node"];
+
+  /**
+   * Access to the incoming request url (pathname+search).
+   *
+   * @deprecated Use `event.url` instead.
+   *
+   * Example: `/api/hello?name=world`
+   * */
+  readonly path: string;
 
-  _headersInit?: HeadersInit;
-  _headers?: Headers;
+  /**
+   * Access to the incoming request method.
+   *
+   * @deprecated Use `event.req.method` instead.
+   */
+  readonly method: string;
 
+  /**
+   * Access to the incoming request headers.
+   *
+   * @deprecated Use `event.req.headers` instead.
+   *
+   * */
   readonly headers: Headers;
-
-  setHeader(name: string, value: string): void;
 }
diff --git a/src/types/utils/fingerprint.ts b/src/types/utils/fingerprint.ts
index c30efdc..2063d3c 100644
--- a/src/types/utils/fingerprint.ts
+++ b/src/types/utils/fingerprint.ts
@@ -12,7 +12,7 @@ export interface RequestFingerprintOptions {
   method?: boolean;
 
   /** @default `false` */
-  path?: boolean;
+  url?: boolean;
 
   /** @default `false` */
   userAgent?: boolean;
diff --git a/src/url.ts b/src/url.ts
new file mode 100644
index 0000000..80db72c
--- /dev/null
+++ b/src/url.ts
@@ -0,0 +1,117 @@
+export const FastURL = /* @__PURE__ */ (() => {
+  const FastURL = class URL implements globalThis.URL {
+    #originalURL: string;
+    #parsedURL: globalThis.URL | undefined;
+
+    _pathname?: string;
+    _urlqindex?: number;
+    _query?: URLSearchParams;
+    _search?: string;
+
+    constructor(url: string) {
+      this.#originalURL = url;
+    }
+
+    get _url(): globalThis.URL {
+      if (!this.#parsedURL) {
+        this.#parsedURL = new globalThis.URL(this.#originalURL);
+      }
+      return this.#parsedURL;
+    }
+
+    toString(): string {
+      return this._url.toString();
+    }
+
+    toJSON(): string {
+      return this.toString();
+    }
+
+    get pathname() {
+      if (this.#parsedURL) {
+        return this.#parsedURL.pathname;
+      }
+      if (!this._pathname) {
+        const url = this.#originalURL;
+        const protoIndex = url.indexOf("://");
+        if (protoIndex === -1) {
+          return this._url.pathname; // deoptimize
+        }
+        const pIndex = url.indexOf("/", protoIndex + 4 /* :// */);
+        if (pIndex === -1) {
+          return this._url.pathname; // deoptimize
+        }
+        const qIndex = (this._urlqindex = url.indexOf("?", pIndex));
+        this._pathname = url.slice(pIndex, qIndex === -1 ? undefined : qIndex);
+      }
+      return this._pathname!;
+    }
+
+    set pathname(value: string) {
+      this._url.pathname = value;
+    }
+
+    get searchParams() {
+      if (this.#parsedURL) {
+        return this.#parsedURL.searchParams;
+      }
+      if (!this._query) {
+        this._query = new URLSearchParams(this.search);
+      }
+      return this._query;
+    }
+
+    get search() {
+      if (this.#parsedURL) {
+        return this.#parsedURL.search;
+      }
+      if (!this._search) {
+        const qIndex = this._urlqindex;
+        if (qIndex === -1) {
+          this._search = "";
+        } else {
+          this._search =
+            qIndex === undefined
+              ? this._url.search // deoptimize (mostly unlikely unless pathname is not accessed)
+              : this.#originalURL.slice(this._urlqindex);
+        }
+      }
+      return this._search;
+    }
+
+    set search(value: string) {
+      this._url.search = value;
+    }
+
+    declare hash: string;
+    declare host: string;
+    declare hostname: string;
+    declare href: string;
+    declare origin: string;
+    declare password: string;
+    declare port: string;
+    declare protocol: string;
+    declare username: string;
+  };
+
+  // prettier-ignore
+  const slowProps = [
+    "hash", "host", "hostname", "href", "origin",
+    "password", "port", "protocol", "username"
+  ] as const;
+
+  for (const prop of slowProps) {
+    Object.defineProperty(FastURL.prototype, prop, {
+      get() {
+        return this._url[prop];
+      },
+      set(value) {
+        this._url[prop] = value;
+      },
+    });
+  }
+
+  Object.setPrototypeOf(FastURL, globalThis.URL);
+
+  return FastURL;
+})();
diff --git a/src/utils/base.ts b/src/utils/base.ts
index 89bec88..cb86d02 100644
--- a/src/utils/base.ts
+++ b/src/utils/base.ts
@@ -20,7 +20,7 @@ export function withBase(base: string, input: EventHandler | H3): EventHandler {
 
   const _handler: EventHandler = async (event) => {
     const _pathBefore = event.url.pathname || "/";
-    event.url.pathname = withoutBase(event.pathname || "/", base);
+    event.url.pathname = withoutBase(event.url.pathname || "/", base);
     return Promise.resolve(_originalHandler(event)).finally(() => {
       event.url.pathname = _pathBefore;
     });
diff --git a/src/utils/body.ts b/src/utils/body.ts
index 92e2391..8f6d85a 100644
--- a/src/utils/body.ts
+++ b/src/utils/body.ts
@@ -21,12 +21,12 @@ export async function readBody<
   _Event extends H3Event = H3Event,
   _T = InferEventInput<"body", _Event, T>,
 >(event: _Event): Promise<undefined | _T> {
-  const text = await event.request.text();
+  const text = await event.req.text();
   if (!text) {
     return undefined;
   }
 
-  const contentType = event.request.headers.get("content-type") || "";
+  const contentType = event.req.headers.get("content-type") || "";
   if (contentType.startsWith("application/x-www-form-urlencoded")) {
     return parseURLEncodedBody(text) as _T;
   }
diff --git a/src/utils/cache.ts b/src/utils/cache.ts
index a52c23f..3d2f447 100644
--- a/src/utils/cache.ts
+++ b/src/utils/cache.ts
@@ -18,25 +18,25 @@ export function handleCacheHeaders(
 
   if (opts.modifiedTime) {
     const modifiedTime = new Date(opts.modifiedTime);
-    const ifModifiedSince = event.request.headers.get("if-modified-since");
-    event.response.headers.set("last-modified", modifiedTime.toUTCString());
+    const ifModifiedSince = event.req.headers.get("if-modified-since");
+    event.res.headers.set("last-modified", modifiedTime.toUTCString());
     if (ifModifiedSince && new Date(ifModifiedSince) >= opts.modifiedTime) {
       cacheMatched = true;
     }
   }
 
   if (opts.etag) {
-    event.response.headers.set("etag", opts.etag);
-    const ifNonMatch = event.request.headers.get("if-none-match");
+    event.res.headers.set("etag", opts.etag);
+    const ifNonMatch = event.req.headers.get("if-none-match");
     if (ifNonMatch === opts.etag) {
       cacheMatched = true;
     }
   }
 
-  event.response.headers.set("cache-control", cacheControls.join(", "));
+  event.res.headers.set("cache-control", cacheControls.join(", "));
 
   if (cacheMatched) {
-    event.response.status = 304;
+    event.res.status = 304;
     return true;
   }
 
diff --git a/src/utils/cookie.ts b/src/utils/cookie.ts
index d3e2b76..8c528e5 100644
--- a/src/utils/cookie.ts
+++ b/src/utils/cookie.ts
@@ -15,7 +15,7 @@ import {
  * ```
  */
 export function parseCookies(event: H3Event): Record<string, string> {
-  return parseCookie(event.request.headers.get("cookie") || "");
+  return parseCookie(event.req.headers.get("cookie") || "");
 }
 
 /**
@@ -51,9 +51,9 @@ export function setCookie(
   const newCookie = serializeCookie(name, value, { path: "/", ...options });
 
   // Check and add only not any other set-cookie headers already set
-  const currentCookies = event.response.headers.getSetCookie();
+  const currentCookies = event.res.headers.getSetCookie();
   if (currentCookies.length === 0) {
-    event.response.headers.set("set-cookie", newCookie);
+    event.res.headers.set("set-cookie", newCookie);
     return;
   }
 
@@ -62,7 +62,7 @@ export function setCookie(
     name,
     (options || {}) as SetCookie,
   );
-  event.response.headers.delete("set-cookie");
+  event.res.headers.delete("set-cookie");
   for (const cookie of currentCookies) {
     const _key = _getDistinctCookieKey(
       cookie.split("=")?.[0],
@@ -71,9 +71,9 @@ export function setCookie(
     if (_key === newCookieKey) {
       continue;
     }
-    event.response.headers.append("set-cookie", cookie);
+    event.res.headers.append("set-cookie", cookie);
   }
-  event.response.headers.append("set-cookie", newCookie);
+  event.res.headers.append("set-cookie", newCookie);
 }
 
 /**
diff --git a/src/utils/cors.ts b/src/utils/cors.ts
index afe7e78..376569b 100644
--- a/src/utils/cors.ts
+++ b/src/utils/cors.ts
@@ -17,15 +17,13 @@ export { isCorsOriginAllowed } from "./internal/cors";
  * Check if the incoming request is a CORS preflight request.
  */
 export function isPreflightRequest(event: H3Event): boolean {
-  const origin = event.request.headers.get("origin");
-  const accessControlRequestMethod = event.request.headers.get(
+  const origin = event.req.headers.get("origin");
+  const accessControlRequestMethod = event.req.headers.get(
     "access-control-request-method",
   );
 
   return (
-    event.request.method === "OPTIONS" &&
-    !!origin &&
-    !!accessControlRequestMethod
+    event.req.method === "OPTIONS" && !!origin && !!accessControlRequestMethod
   );
 }
 
@@ -44,7 +42,7 @@ export function appendCorsPreflightHeaders(
     ...createMaxAgeHeader(options),
   };
   for (const [key, value] of Object.entries(headers)) {
-    event.response.headers.append(key, value);
+    event.res.headers.append(key, value);
   }
 }
 
@@ -58,7 +56,7 @@ export function appendCorsHeaders(event: H3Event, options: H3CorsOptions) {
     ...createExposeHeaders(options),
   };
   for (const [key, value] of Object.entries(headers)) {
-    event.response.headers.append(key, value);
+    event.res.headers.append(key, value);
   }
 }
 
diff --git a/src/utils/event.ts b/src/utils/event.ts
index bedfe8d..d250dda 100644
--- a/src/utils/event.ts
+++ b/src/utils/event.ts
@@ -1,4 +1,4 @@
-import { H3WebEvent } from "../event";
+import { _H3Event } from "../event";
 import type { H3EventContext, H3Event } from "../types";
 
 /**
@@ -31,5 +31,5 @@ export function mockEvent(
   } else {
     request = _request;
   }
-  return new H3WebEvent(request);
+  return new _H3Event(request);
 }
diff --git a/src/utils/fingerprint.ts b/src/utils/fingerprint.ts
index 3efc0a5..9e9c907 100644
--- a/src/utils/fingerprint.ts
+++ b/src/utils/fingerprint.ts
@@ -21,15 +21,15 @@ export async function getRequestFingerprint(
   }
 
   if (opts.method === true) {
-    fingerprint.push(event.request.method);
+    fingerprint.push(event.req.method);
   }
 
-  if (opts.path === true) {
-    fingerprint.push(event.path);
+  if (opts.url === true) {
+    fingerprint.push(event.url.href);
   }
 
   if (opts.userAgent === true) {
-    fingerprint.push(event.request.headers.get("user-agent"));
+    fingerprint.push(event.req.headers.get("user-agent"));
   }
 
   const fingerprintString = fingerprint.filter(Boolean).join("|");
diff --git a/src/utils/internal/cors.ts b/src/utils/internal/cors.ts
index 4648e44..85f1bed 100644
--- a/src/utils/internal/cors.ts
+++ b/src/utils/internal/cors.ts
@@ -77,7 +77,7 @@ export function createOriginHeaders(
   options: H3CorsOptions,
 ): H3AccessControlAllowOriginHeader {
   const { origin: originOption } = options;
-  const origin = event.request.headers.get("origin");
+  const origin = event.req.headers.get("origin");
 
   if (!origin || !originOption || originOption === "*") {
     return { "access-control-allow-origin": "*" };
@@ -138,7 +138,7 @@ export function createAllowHeaderHeaders(
   const { allowHeaders } = options;
 
   if (!allowHeaders || allowHeaders === "*" || allowHeaders.length === 0) {
-    const header = event.request.headers.get("access-control-request-headers");
+    const header = event.req.headers.get("access-control-request-headers");
 
     return header
       ? {
diff --git a/src/utils/internal/event-stream.ts b/src/utils/internal/event-stream.ts
index dde1025..66acf4d 100644
--- a/src/utils/internal/event-stream.ts
+++ b/src/utils/internal/event-stream.ts
@@ -146,7 +146,7 @@ export class EventStream {
 
   async send(): Promise<BodyInit> {
     setEventStreamHeaders(this._event);
-    this._event.response.status = 200;
+    this._event.res.status = 200;
     this._handled = true;
     return this._transformStream.readable;
   }
@@ -185,21 +185,19 @@ export function formatEventStreamMessages(
 }
 
 export function setEventStreamHeaders(event: H3Event) {
-  event.response.headers.set("content-type", "text/event-stream");
-  event.response.headers.set(
+  event.res.headers.set("content-type", "text/event-stream");
+  event.res.headers.set(
     "cache-control",
     "private, no-cache, no-store, no-transform, must-revalidate, max-age=0",
   );
   // prevent nginx from buffering the response
-  event.response.headers.set("x-accel-buffering", "no");
+  event.res.headers.set("x-accel-buffering", "no");
 
   if (!isHttp2Request(event)) {
-    event.response.headers.set("connection", "keep-alive");
+    event.res.headers.set("connection", "keep-alive");
   }
 }
 
 export function isHttp2Request(event: H3Event) {
-  return (
-    event.request.headers.has(":path") || event.request.headers.has(":method")
-  );
+  return event.req.headers.has(":path") || event.req.headers.has(":method");
 }
diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts
index bac3420..98a60cb 100644
--- a/src/utils/proxy.ts
+++ b/src/utils/proxy.ts
@@ -22,17 +22,17 @@ export async function proxyRequest(
   // Request Body
   let body;
   let duplex: Duplex | undefined;
-  if (PayloadMethods.has(event.request.method)) {
+  if (PayloadMethods.has(event.req.method)) {
     if (opts.streamRequest) {
-      body = event.request.body;
+      body = event.req.body;
       duplex = "half";
     } else {
-      body = await event.request.arrayBuffer();
+      body = await event.req.arrayBuffer();
     }
   }
 
   // Method
-  const method = opts.fetchOptions?.method || event.request.method;
+  const method = opts.fetchOptions?.method || event.req.method;
 
   // Headers
   const fetchHeaders = mergeHeaders(
@@ -75,11 +75,8 @@ export async function proxy(
       cause: error,
     });
   }
-  event.response.status = sanitizeStatusCode(
-    response.status,
-    event.response.status,
-  );
-  event.response.statusText = sanitizeStatusMessage(response.statusText);
+  event.res.status = sanitizeStatusCode(response.status, event.res.status);
+  event.res.statusText = sanitizeStatusMessage(response.statusText);
 
   const cookies: string[] = [];
 
@@ -94,7 +91,7 @@ export async function proxy(
       cookies.push(...splitSetCookieString(value));
       continue;
     }
-    event.response.headers.set(key, value);
+    event.res.headers.set(key, value);
   }
 
   if (cookies.length > 0) {
@@ -112,7 +109,7 @@ export async function proxy(
       return cookie;
     });
     for (const cookie of _cookies) {
-      event.response.headers.append("set-cookie", cookie);
+      event.res.headers.append("set-cookie", cookie);
     }
   }
 
@@ -142,7 +139,7 @@ export function getProxyRequestHeaders(
   opts?: { host?: boolean },
 ) {
   const headers = new EmptyObject();
-  for (const [name, value] of event.request.headers.entries()) {
+  for (const [name, value] of event.req.headers.entries()) {
     if (!ignoredHeaders.has(name) || (name === "host" && opts?.host)) {
       headers[name] = value;
     }
diff --git a/src/utils/request.ts b/src/utils/request.ts
index 28007f5..2e48ae0 100644
--- a/src/utils/request.ts
+++ b/src/utils/request.ts
@@ -21,7 +21,7 @@ export function getQuery<
   Event extends H3Event = H3Event,
   _T = Exclude<InferEventInput<"query", Event, T>, undefined>,
 >(event: Event): _T {
-  return parseQuery(event.queryString.slice(1)) as _T;
+  return parseQuery(event.url.search.slice(1)) as _T;
 }
 
 /**
@@ -158,15 +158,15 @@ export function isMethod(
   expected: HTTPMethod | HTTPMethod[],
   allowHead?: boolean,
 ) {
-  if (allowHead && event.request.method === "HEAD") {
+  if (allowHead && event.req.method === "HEAD") {
     return true;
   }
 
   if (typeof expected === "string") {
-    if (event.request.method === expected) {
+    if (event.req.method === expected) {
       return true;
     }
-  } else if (expected.includes(event.request.method as HTTPMethod)) {
+  } else if (expected.includes(event.req.method as HTTPMethod)) {
     return true;
   }
 
@@ -216,12 +216,12 @@ export function getRequestHost(
   opts: { xForwardedHost?: boolean } = {},
 ) {
   if (opts.xForwardedHost) {
-    const xForwardedHost = event.request.headers.get("x-forwarded-host");
+    const xForwardedHost = event.req.headers.get("x-forwarded-host");
     if (xForwardedHost) {
       return xForwardedHost;
     }
   }
-  return event.request.headers.get("host") || "";
+  return event.req.headers.get("host") || "";
 }
 
 /**
@@ -241,7 +241,7 @@ export function getRequestProtocol(
   opts: { xForwardedProto?: boolean } = {},
 ) {
   if (opts.xForwardedProto !== false) {
-    const forwardedProto = event.request.headers.get("x-forwarded-proto");
+    const forwardedProto = event.req.headers.get("x-forwarded-proto");
     if (forwardedProto === "https") {
       return "https";
     }
@@ -253,7 +253,7 @@ export function getRequestProtocol(
 }
 
 /**
- * Generated the full incoming request URL using `getRequestProtocol`, `getRequestHost` and `event.path`.
+ * Generated the full incoming request URL.
  *
  * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
  *
@@ -307,12 +307,12 @@ export function getRequestIP(
 ): string | undefined {
   if (opts.xForwardedFor) {
     // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For#syntax
-    const _header = event.request.headers.get("x-forwarded-for");
+    const _header = event.req.headers.get("x-forwarded-for");
     const xForwardedFor = (_header || "")?.split(",").shift()?.trim();
     if (xForwardedFor) {
       return xForwardedFor;
     }
   }
 
-  return event.context.clientAddress || event.ip || undefined;
+  return event.context.clientAddress || event.req.remoteAddress || undefined;
 }
diff --git a/src/utils/response.ts b/src/utils/response.ts
index b3df908..9906b1b 100644
--- a/src/utils/response.ts
+++ b/src/utils/response.ts
@@ -17,18 +17,18 @@ import {
  * @param code status code to be send. By default, it is `204 No Content`.
  */
 export function noContent(event: H3Event, code?: StatusCode): "" {
-  const currentStatus = event.response.status;
+  const currentStatus = event.res.status;
 
   if (!code && currentStatus && currentStatus !== 200) {
-    code = event.response.status;
+    code = event.res.status;
   }
 
-  event.response.status = sanitizeStatusCode(code, 204);
+  event.res.status = sanitizeStatusCode(code, 204);
 
   // 204 responses MUST NOT have a Content-Length header field
   // https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2
-  if (event.response.status === 204) {
-    event.response.headers.delete("content-length");
+  if (event.res.status === 204) {
+    event.res.headers.delete("content-length");
   }
 
   return "";
@@ -56,12 +56,12 @@ export function redirect(
   location: string,
   code: StatusCode = 302,
 ) {
-  event.response.status = sanitizeStatusCode(code, event.response.status);
-  event.response.headers.set("location", location);
+  event.res.status = sanitizeStatusCode(code, event.res.status);
+  event.res.headers.set("location", location);
   const encodedLoc = location.replace(/"/g, "%22");
   const html = `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${encodedLoc}"></head></html>`;
-  if (!event.response.headers.has("content-type")) {
-    event.response.headers.set("content-type", "text/html");
+  if (!event.res.headers.has("content-type")) {
+    event.res.headers.set("content-type", "text/html");
   }
   return html;
 }
diff --git a/src/utils/session.ts b/src/utils/session.ts
index 9b151f1..30efe03 100644
--- a/src/utils/session.ts
+++ b/src/utils/session.ts
@@ -73,7 +73,7 @@ export async function getSession<T extends SessionData = SessionData>(
       typeof config.sessionHeader === "string"
         ? config.sessionHeader.toLowerCase()
         : `x-${sessionName.toLowerCase()}-session`;
-    const headerValue = event.request.headers.get(headerName);
+    const headerValue = event.req.headers.get(headerName);
     if (typeof headerValue === "string") {
       sealedSession = headerValue;
     }
diff --git a/src/utils/static.ts b/src/utils/static.ts
index 65594c8..dbb5b6a 100644
--- a/src/utils/static.ts
+++ b/src/utils/static.ts
@@ -1,10 +1,6 @@
 import type { H3Event, StaticAssetMeta, ServeStaticOptions } from "../types";
 import { createError } from "../error";
-import {
-  withLeadingSlash,
-  withoutTrailingSlash,
-  getPathname,
-} from "./internal/path";
+import { withLeadingSlash, withoutTrailingSlash } from "./internal/path";
 
 /**
  * Dynamically serve static assets based on the request path.
@@ -13,9 +9,9 @@ export async function serveStatic(
   event: H3Event,
   options: ServeStaticOptions,
 ): Promise<false | undefined | null | BodyInit> {
-  if (event.request.method !== "GET" && event.request.method !== "HEAD") {
+  if (event.req.method !== "GET" && event.req.method !== "HEAD") {
     if (!options.fallthrough) {
-      event.response.headers.set("allow", "GET, HEAD");
+      event.res.headers.set("allow", "GET, HEAD");
       throw createError({
         statusMessage: "Method Not Allowed",
         statusCode: 405,
@@ -25,16 +21,16 @@ export async function serveStatic(
   }
 
   const originalId = decodeURI(
-    withLeadingSlash(withoutTrailingSlash(getPathname(event.path))),
+    withLeadingSlash(withoutTrailingSlash(event.url.pathname)),
   );
 
   const acceptEncodings = parseAcceptEncoding(
-    event.request.headers.get("accept-encoding") || "",
+    event.req.headers.get("accept-encoding") || "",
     options.encodings,
   );
 
   if (acceptEncodings.length > 1) {
-    event.response.headers.set("vary", "accept-encoding");
+    event.res.headers.set("vary", "accept-encoding");
   }
 
   let id = originalId;
@@ -65,50 +61,50 @@ export async function serveStatic(
     return false;
   }
 
-  if (meta.etag && !event.response.headers.has("etag")) {
-    event.response.headers.set("etag", meta.etag);
+  if (meta.etag && !event.res.headers.has("etag")) {
+    event.res.headers.set("etag", meta.etag);
   }
 
   const ifNotMatch =
-    meta.etag && event.request.headers.get("if-none-match") === meta.etag;
+    meta.etag && event.req.headers.get("if-none-match") === meta.etag;
   if (ifNotMatch) {
-    event.response.status = 304;
-    event.response.statusText = "Not Modified";
+    event.res.status = 304;
+    event.res.statusText = "Not Modified";
     return "";
   }
 
   if (meta.mtime) {
     const mtimeDate = new Date(meta.mtime);
 
-    const ifModifiedSinceH = event.request.headers.get("if-modified-since");
+    const ifModifiedSinceH = event.req.headers.get("if-modified-since");
     if (ifModifiedSinceH && new Date(ifModifiedSinceH) >= mtimeDate) {
-      event.response.status = 304;
-      event.response.statusText = "Not Modified";
+      event.res.status = 304;
+      event.res.statusText = "Not Modified";
       return "";
     }
 
-    if (!event.response.headers.get("last-modified")) {
-      event.response.headers.set("last-modified", mtimeDate.toUTCString());
+    if (!event.res.headers.get("last-modified")) {
+      event.res.headers.set("last-modified", mtimeDate.toUTCString());
     }
   }
 
-  if (meta.type && !event.response.headers.get("content-type")) {
-    event.response.headers.set("content-type", meta.type);
+  if (meta.type && !event.res.headers.get("content-type")) {
+    event.res.headers.set("content-type", meta.type);
   }
 
-  if (meta.encoding && !event.response.headers.get("content-encoding")) {
-    event.response.headers.set("content-encoding", meta.encoding);
+  if (meta.encoding && !event.res.headers.get("content-encoding")) {
+    event.res.headers.set("content-encoding", meta.encoding);
   }
 
   if (
     meta.size !== undefined &&
     meta.size > 0 &&
-    !event.request.headers.get("content-length")
+    !event.req.headers.get("content-length")
   ) {
-    event.response.headers.set("content-length", meta.size + "");
+    event.res.headers.set("content-length", meta.size + "");
   }
 
-  if (event.request.method === "HEAD") {
+  if (event.req.method === "HEAD") {
     return "";
   }
 


Test patch:
diff --git a/test/app.test.ts b/test/app.test.ts
index 155c867..3d5b999 100644
--- a/test/app.test.ts
+++ b/test/app.test.ts
@@ -4,7 +4,7 @@ import { describeMatrix } from "./_setup";
 
 describeMatrix("app", (t, { it, expect }) => {
   it("can return JSON directly", async () => {
-    t.app.get("/api", (event) => ({ url: event.path }));
+    t.app.get("/api", (event) => ({ url: event.url.pathname }));
     const res = await t.fetch("/api");
 
     expect(await res.json()).toEqual({ url: "/api" });
@@ -174,7 +174,7 @@ describeMatrix("app", (t, { it, expect }) => {
 
   it("allows overriding Content-Type", async () => {
     t.app.use((event) => {
-      event.response.setHeader("content-type", "text/xhtml");
+      event.res.headers.set("content-type", "text/xhtml");
       return "<h1>Hello world!</h1>";
     });
     const res = await t.fetch("/");
diff --git a/test/bench/bench.impl.ts b/test/bench/bench.impl.ts
index 0f296e9..16979cf 100644
--- a/test/bench/bench.impl.ts
+++ b/test/bench/bench.impl.ts
@@ -35,14 +35,19 @@ export function h3(lib: typeof _h3src, useRes?: boolean) {
 
     // [POST] /json
     app.post("/json", (event) =>
-      event.request.json().then(
-        (jsonBody) =>
-          new Response(JSON.stringify(jsonBody), {
-            headers: {
-              "content-type": "application/json; charset=utf-8",
-            },
-          }),
-      ),
+      (
+        event.req ||
+        (event as unknown as { request: Request }) /* nightly */.request
+      )
+        .json()
+        .then(
+          (jsonBody) =>
+            new Response(JSON.stringify(jsonBody), {
+              headers: {
+                "content-type": "application/json; charset=utf-8",
+              },
+            }),
+        ),
     );
   } else {
     // [GET] /
@@ -50,14 +55,24 @@ export function h3(lib: typeof _h3src, useRes?: boolean) {
 
     // [GET] /id/:id
     app.get("/id/:id", (event) => {
-      event.response.setHeader("x-powered-by", "benchmark");
+      if ((event as any).response?.setHeader) {
+        // mightly
+        (event as any).response.setHeader("x-powered-by", "benchmark");
+      } else {
+        event.res.headers.set("x-powered-by", "benchmark");
+      }
       // const name = event.query.get("name");
       const name = lib.getQuery(event).name;
       return `${event.context.params!.id} ${name}`;
     });
 
     // [POST] /json
-    app.post("/json", (event) => event.request.json());
+    app.post("/json", (event) =>
+      (
+        event.req ||
+        (event as unknown as { request: Request }) /* nightly */.request
+      ).json(),
+    );
   }
 
   return app.fetch;
@@ -75,13 +90,18 @@ export function h3Middleware(lib: typeof _h3src) {
 
   // [GET] /id/:id
   app.use("/id/:id", (event) => {
-    event.response.setHeader("x-powered-by", "benchmark");
+    event.res.headers.set("x-powered-by", "benchmark");
     const name = lib.getQuery(event).name;
     return `${event.context.params!.id} ${name}`;
   });
 
   // [POST] /json
-  app.use("/json", (event) => event.request.json());
+  app.use("/json", (event) =>
+    (
+      event.req ||
+      (event as unknown as { request: Request }) /* nightly */.request
+    ).json(),
+  );
 
   return app.fetch;
 }
diff --git a/test/bench/h3.mjs b/test/bench/h3.mjs
index 182c674..14c6811 100644
--- a/test/bench/h3.mjs
+++ b/test/bench/h3.mjs
@@ -1,4 +1,4 @@
-import { bench, summary, run } from "mitata";
+import { bench, compact, summary, run } from "mitata";
 import { requests } from "./input.mjs";
 
 import * as _dist from "../../dist/index.mjs";
@@ -11,25 +11,27 @@ const preparedRequests = requests.map((request) => {
   });
 });
 
-summary(async () => {
-  for (const [name, { createH3, getQuery }] of Object.entries({
-    _dist,
-    _nightly,
-  })) {
-    bench(name, function* () {
-      const app = createH3()
-        .get("/", () => "Hi")
-        .get("/id/:id", (event) => {
-          event.response.setHeader("x-powered-by", "benchmark");
-          return `${event.context.params.id} ${getQuery(event).name}`;
-        })
-        .post("/json", (event) => event.request.json());
+summary(() => {
+  compact(() => {
+    for (const [name, { createH3, getQuery }] of Object.entries({
+      _dist,
+      _nightly,
+    })) {
+      bench(name, function* () {
+        const app = createH3()
+          .get("/", () => "Hi")
+          .get("/id/:id", (event) => {
+            event.res.headers.set("x-powered-by", "benchmark");
+            return `${event.context.params.id} ${getQuery(event).name}`;
+          })
+          .post("/json", (event) => event.req.json());
 
-      yield async () => {
-        await Promise.all(preparedRequests.map((req) => app.fetch(req)));
-      };
-    }).gc("inner");
-  }
+        yield async () => {
+          await Promise.all(preparedRequests.map((req) => app.fetch(req)));
+        };
+      }).gc("inner");
+    }
+  });
 });
 
 await run({ throw: true });
diff --git a/test/bench/url.ts b/test/bench/url.ts
new file mode 100644
index 0000000..7bd6898
--- /dev/null
+++ b/test/bench/url.ts
@@ -0,0 +1,34 @@
+import { bench, compact, summary, group, run, do_not_optimize } from "mitata";
+import { FastURL } from "../../src/url";
+
+const input = "https://user:password@example.com/path/to/resource?query=string";
+
+const scenarios = {
+  pathname: (url: URL) => do_not_optimize([url.pathname]),
+  params: (url: URL) => do_not_optimize([url.searchParams.get("query")]),
+  "pathname+params": (url: URL) =>
+    do_not_optimize([url.pathname, url.searchParams.get("query")]),
+  "pathname+params+username": (url: URL) =>
+    do_not_optimize([
+      url.pathname,
+      url.searchParams.get("query"),
+      url.username,
+    ]),
+};
+
+for (const [name, fn] of Object.entries(scenarios)) {
+  group(name, () => {
+    summary(() => {
+      compact(() => {
+        bench("globalThis.URL", () => do_not_optimize(fn(new URL(input)))).gc(
+          "inner",
+        );
+        bench("FastURL", () => do_not_optimize(fn(new FastURL(input)))).gc(
+          "inner",
+        );
+      });
+    });
+  });
+}
+
+await run({ throw: true });
diff --git a/test/body.test.ts b/test/body.test.ts
index 5ff31b7..67e5411 100644
--- a/test/body.test.ts
+++ b/test/body.test.ts
@@ -6,7 +6,7 @@ import { describeMatrix } from "./_setup";
 describeMatrix("body", (t, { it, expect, describe }) => {
   it("can read simple string", async () => {
     t.app.all("/api/test", async (event) => {
-      const body = await event.request.text();
+      const body = await event.req.text();
       expect(body).toEqual('{"bool":true,"name":"string","number":1}');
       return "200";
     });
@@ -25,7 +25,7 @@ describeMatrix("body", (t, { it, expect, describe }) => {
   it("can read chunked string", async () => {
     const requestJsonUrl = new URL("assets/sample.json", import.meta.url);
     t.app.all("/api/test", async (event) => {
-      const body = await event.request.text();
+      const body = await event.req.text();
       const json = (await readFile(requestJsonUrl)).toString("utf8");
 
       expect(body).toEqual(json);
@@ -55,7 +55,7 @@ describeMatrix("body", (t, { it, expect, describe }) => {
   it("returns empty string if body is not present", async () => {
     let _body: string | undefined = "initial";
     t.app.all("/api/test", async (event) => {
-      _body = await event.request.text();
+      _body = await event.req.text();
       return "200";
     });
     const res = await t.fetch("/api/test", {
@@ -187,7 +187,7 @@ describeMatrix("body", (t, { it, expect, describe }) => {
 
   it("parses multipart form data", async () => {
     t.app.all("/api/test", async (event) => {
-      const formData = await event.request.formData();
+      const formData = await event.req.formData();
       return [...formData.entries()].map(([name, value]) => ({
         name,
         data: value,
@@ -222,7 +222,7 @@ describeMatrix("body", (t, { it, expect, describe }) => {
   it("returns empty string if body is not present with text/plain", async () => {
     let _body: string | undefined;
     t.app.all("/api/test", async (event) => {
-      _body = await event.request.text();
+      _body = await event.req.text();
       return "200";
     });
     const result = await t.fetch("/api/test", {
@@ -256,7 +256,7 @@ describeMatrix("body", (t, { it, expect, describe }) => {
   it("returns the string if content type is text/*", async () => {
     let _body: string | undefined;
     t.app.all("/api/test", async (event) => {
-      _body = await event.request.text();
+      _body = await event.req.text();
       return "200";
     });
     const result = await t.fetch("/api/test", {
@@ -273,7 +273,7 @@ describeMatrix("body", (t, { it, expect, describe }) => {
 
   it("returns string as is if cannot parse with unknown content type", async () => {
     t.app.all("/api/test", async (event) => {
-      const _body = await event.request.text();
+      const _body = await event.req.text();
       return _body;
     });
     const result = await t.fetch("/api/test", {
@@ -310,7 +310,7 @@ describeMatrix("body", (t, { it, expect, describe }) => {
   describe("readFormDataBody", () => {
     it("can handle form as FormData in event handler", async () => {
       t.app.all("/api/*", async (event) => {
-        const formData = await event.request.formData();
+        const formData = await event.req.formData();
         const user = formData!.get("user");
         expect(formData instanceof FormData).toBe(true);
         expect(user).toBe("john");
diff --git a/test/event.test.ts b/test/event.test.ts
index 43fb86b..fc4db3b 100644
--- a/test/event.test.ts
+++ b/test/event.test.ts
@@ -4,8 +4,8 @@ import { describeMatrix } from "./_setup";
 describeMatrix("event", (t, { it, expect }) => {
   it("can read the method", async () => {
     t.app.all("/*", (event) => {
-      expect(event.request.method).toBe(event.request.method);
-      expect(event.request.method).toBe("POST");
+      expect(event.req.method).toBe(event.req.method);
+      expect(event.req.method).toBe("POST");
       return "200";
     });
     const result = await t.fetch("/hello", { method: "POST" });
@@ -15,7 +15,7 @@ describeMatrix("event", (t, { it, expect }) => {
   it("can read the headers", async () => {
     t.app.all("/*", (event) => {
       return {
-        headers: [...event.request.headers.entries()],
+        headers: [...event.req.headers.entries()],
       };
     });
     const result = await t.fetch("/hello", {
@@ -46,7 +46,7 @@ describeMatrix("event", (t, { it, expect }) => {
     t.app.all("/*", async (event) => {
       let bytes = 0;
       // @ts-expect-error iterator
-      for await (const chunk of event.request.body!) {
+      for await (const chunk of event.req.body!) {
         bytes += chunk.length;
       }
       return {
@@ -64,8 +64,8 @@ describeMatrix("event", (t, { it, expect }) => {
 
   it("can convert to a web request", async () => {
     t.app.all("/", async (event) => {
-      expect(event.request.method).toBe("POST");
-      expect(event.request.headers.get("x-test")).toBe("123");
+      expect(event.req.method).toBe("POST");
+      expect(event.req.headers.get("x-test")).toBe("123");
       expect(await readBody(event)).toMatchObject({ hello: "world" });
       return "200";
     });
diff --git a/test/fixture/app.ts b/test/fixture/app.ts
index 315c28b..e941a39 100644
--- a/test/fixture/app.ts
+++ b/test/fixture/app.ts
@@ -5,10 +5,10 @@ export const app = createH3();
 app.get("/**", (event) => {
   return {
     request: {
-      method: event.request.method,
-      path: event.path,
+      method: event.req.method,
+      path: event.url.pathname + event.url.search,
       params: getQuery(event),
-      headers: Object.fromEntries(event.request.headers.entries()),
+      headers: Object.fromEntries(event.req.headers.entries()),
     },
   };
 });
diff --git a/test/integrations.test.ts b/test/integrations.test.ts
index 2c8c31d..4d89ceb 100644
--- a/test/integrations.test.ts
+++ b/test/integrations.test.ts
@@ -119,7 +119,7 @@ describeMatrix("integrations", (t, { it, expect, describe }) => {
       const connectApp = createConnectApp();
       const router = createH3().get(
         "/hello",
-        (event) => event.query.get("x") ?? "hello",
+        (event) => event.url.searchParams.get("x") ?? "hello",
       );
       t.app.use("/api/**", withBase("/api", router));
       connectApp.use("/api", toNodeHandler(t.app));
diff --git a/test/proxy.test.ts b/test/proxy.test.ts
index 341675e..eb35ce8 100644
--- a/test/proxy.test.ts
+++ b/test/proxy.test.ts
@@ -24,10 +24,10 @@ describeMatrix("proxy", (t, { it, expect, describe }) => {
     describe("proxyRequest()", () => {
       it("can proxy request", async () => {
         t.app.all("/debug", async (event) => {
-          const headers = Object.fromEntries(event.request.headers.entries());
-          const body = await event.request.text();
+          const headers = Object.fromEntries(event.req.headers.entries());
+          const body = await event.req.text();
           return {
-            method: event.request.method,
+            method: event.req.method,
             headers,
             body,
           };
@@ -73,15 +73,15 @@ describeMatrix("proxy", (t, { it, expect, describe }) => {
 
       it("can proxy binary request", async () => {
         t.app.all("/debug", async (event) => {
-          const body = await event.request.arrayBuffer();
+          const body = await event.req.arrayBuffer();
           return {
-            headers: Object.fromEntries(event.request.headers.entries()),
+            headers: Object.fromEntries(event.req.headers.entries()),
             bytes: body.byteLength,
           };
         });
 
         t.app.all("/", (event) => {
-          event.response.headers.set("x-res-header", "works");
+          event.res.headers.set("x-res-header", "works");
           return proxyRequest(event, t.url + "/debug", { fetch });
         });
 
@@ -106,8 +106,8 @@ describeMatrix("proxy", (t, { it, expect, describe }) => {
       it("can proxy stream request", async () => {
         t.app.all("/debug", async (event) => {
           return {
-            body: await event.request.text(),
-            headers: Object.fromEntries(event.request.headers.entries()),
+            body: await event.req.text(),
+            headers: Object.fromEntries(event.req.headers.entries()),
           };
         });
 
@@ -151,7 +151,7 @@ describeMatrix("proxy", (t, { it, expect, describe }) => {
         const message = '{"hello":"world"}';
 
         t.app.all("/debug", (event) => {
-          event.response.headers.set("content-type", "application/json");
+          event.res.headers.set("content-type", "application/json");
           return message;
         });
 
@@ -215,7 +215,7 @@ describeMatrix("proxy", (t, { it, expect, describe }) => {
     describe("cookieDomainRewrite", () => {
       beforeEach(() => {
         t.app.all("/debug", (event) => {
-          event.response.headers.set(
+          event.res.headers.set(
             "set-cookie",
             "foo=219ffwef9w0f; Domain=somecompany.co.uk; Path=/; Expires=Wed, 30 Aug 2022 00:00:00 GMT",
           );
@@ -257,11 +257,11 @@ describeMatrix("proxy", (t, { it, expect, describe }) => {
 
       it("can rewrite domains of multiple cookies", async () => {
         t.app.all("/multiple/debug", (event) => {
-          event.response.headers.append(
+          event.res.headers.append(
             "set-cookie",
             "foo=219ffwef9w0f; Domain=somecompany.co.uk; Path=/; Expires=Wed, 30 Aug 2022 00:00:00 GMT",
           );
-          event.response.headers.append(
+          event.res.headers.append(
             "set-cookie",
             "bar=38afes7a8; Domain=somecompany.co.uk; Path=/; Expires=Wed, 30 Aug 2022 00:00:00 GMT",
           );
@@ -306,7 +306,7 @@ describeMatrix("proxy", (t, { it, expect, describe }) => {
     describe("cookiePathRewrite", () => {
       beforeEach(() => {
         t.app.all("/debug", (event) => {
-          event.response.headers.set(
+          event.res.headers.set(
             "set-cookie",
             "foo=219ffwef9w0f; Domain=somecompany.co.uk; Path=/; Expires=Wed, 30 Aug 2022 00:00:00 GMT",
           );
@@ -348,11 +348,11 @@ describeMatrix("proxy", (t, { it, expect, describe }) => {
 
       it("can rewrite paths of multiple cookies", async () => {
         t.app.all("/multiple/debug", (event) => {
-          event.response.headers.append(
+          event.res.headers.append(
             "set-cookie",
             "foo=219ffwef9w0f; Domain=somecompany.co.uk; Path=/; Expires=Wed, 30 Aug 2022 00:00:00 GMT",
           );
-          event.response.headers.append(
+          event.res.headers.append(
             "set-cookie",
             "bar=38afes7a8; Domain=somecompany.co.uk; Path=/; Expires=Wed, 30 Aug 2022 00:00:00 GMT",
           );
@@ -408,7 +408,7 @@ describeMatrix("proxy", (t, { it, expect, describe }) => {
           return proxyRequest(event, t.url + "/debug", {
             fetch,
             onResponse(event) {
-              event.response.headers.set("x-custom", "hello");
+              event.res.headers.set("x-custom", "hello");
             },
           });
         });
@@ -424,7 +424,7 @@ describeMatrix("proxy", (t, { it, expect, describe }) => {
             fetch,
             onResponse(_event) {
               return new Promise((resolve) => {
-                resolve(event.response.headers.set("x-custom", "hello"));
+                resolve(event.res.headers.set("x-custom", "hello"));
               });
             },
           });
diff --git a/test/router.test.ts b/test/router.test.ts
index acd1eee..c076a1c 100644
--- a/test/router.test.ts
+++ b/test/router.test.ts
@@ -63,12 +63,9 @@ describeMatrix("router", (t, { it, expect, describe }) => {
   });
 
   it("Handle shadowed route", async () => {
-    t.app.post(
-      "/test/123",
-      (event) => `[${event.request.method}] ${event.path}`,
-    );
+    t.app.post("/test/123", (event) => `[${event.req.method}] ${event.path}`);
 
-    t.app.get("/test/**", (event) => `[${event.request.method}] ${event.path}`);
+    t.app.get("/test/**", (event) => `[${event.req.method}] ${event.path}`);
 
     // Loop to validate cached behavior
     for (let i = 0; i < 5; i++) {
@@ -101,7 +98,9 @@ describeMatrix("router", (t, { it, expect, describe }) => {
       const res = await t.fetch("/preemptive/404");
       expect(JSON.parse(await res.text())).toMatchObject({
         statusCode: 404,
-        statusMessage: "Cannot find any route matching [GET] /preemptive/404",
+        statusMessage: expect.stringMatching(
+          /Cannot find any route matching \[GET\] http:\/\/localhost[:\d]*\/preemptive\/404/,
+        ),
       });
     });
 
diff --git a/test/session.test.ts b/test/session.test.ts
index 842b524..89ca88a 100644
--- a/test/session.test.ts
+++ b/test/session.test.ts
@@ -19,7 +19,7 @@ describeMatrix("session", (t, { it, expect }) => {
     router = createH3({});
     router.use("/", async (event) => {
       const session = await useSession(event, sessionConfig);
-      if (event.request.method === "POST") {
+      if (event.req.method === "POST") {
         await session.update((await readBody(event)) as any);
       }
       return { session };
diff --git a/test/sse.test.ts b/test/sse.test.ts
index c8658ae..0efa4ba 100644
--- a/test/sse.test.ts
+++ b/test/sse.test.ts
@@ -6,7 +6,7 @@ import { describeMatrix } from "./_setup";
 describeMatrix("sse", (t, { it, expect }) => {
   beforeEach(() => {
     t.app.get("/sse", (event) => {
-      const includeMeta = event.query.get("includeMeta") === "true";
+      const includeMeta = event.url.searchParams.get("includeMeta") === "true";
       const eventStream = createEventStream(event);
       let counter = 0;
       const clear = setInterval(() => {
diff --git a/test/status.test.ts b/test/status.test.ts
index 61e1f3a..18015d5 100644
--- a/test/status.test.ts
+++ b/test/status.test.ts
@@ -37,8 +37,8 @@ describeMatrix("event response", (t, { it, describe, expect }) => {
 
     it("override status and statusText", async () => {
       t.app.all("/test", (event) => {
-        event.response.status = 418;
-        event.response.statusText = "custom-status";
+        event.res.status = 418;
+        event.res.statusText = "custom-status";
         return "text";
       });
 
@@ -83,8 +83,8 @@ describeMatrix("event response", (t, { it, describe, expect }) => {
     });
     it("override status and statusText with setResponseStatus method", async () => {
       t.app.all("/test", (event) => {
-        event.response.status = 418;
-        event.response.statusText = "status-text";
+        event.res.status = 418;
+        event.res.statusText = "status-text";
         return "";
       });
 
@@ -103,8 +103,8 @@ describeMatrix("event response", (t, { it, describe, expect }) => {
 
     it("does not sets content-type for 304", async () => {
       t.app.all("/test", (event) => {
-        event.response.status = 304;
-        event.response.statusText = "Not Modified";
+        event.res.status = 304;
+        event.res.statusText = "Not Modified";
         return "";
       });
 
diff --git a/test/unit/cors.test.ts b/test/unit/cors.test.ts
index ebe85e7..61a9b98 100644
--- a/test/unit/cors.test.ts
+++ b/test/unit/cors.test.ts
@@ -468,23 +468,23 @@ describe("cors (unit)", () => {
         appendCorsPreflightHeaders(eventMock, options);
 
         expect(
-          eventMock.response.headers.get("access-control-allow-origin"),
+          eventMock.res.headers.get("access-control-allow-origin"),
         ).toEqual("*");
         expect(
-          eventMock.response.headers.has("access-control-allow-credentials"),
+          eventMock.res.headers.has("access-control-allow-credentials"),
         ).toEqual(false);
         expect(
-          eventMock.response.headers.get("access-control-allow-methods"),
+          eventMock.res.headers.get("access-control-allow-methods"),
         ).toEqual("*");
         expect(
-          eventMock.response.headers.get("access-control-allow-headers"),
+          eventMock.res.headers.get("access-control-allow-headers"),
         ).toEqual("CUSTOM-HEADER");
-        expect(eventMock.response.headers.get("vary")).toEqual(
+        expect(eventMock.res.headers.get("vary")).toEqual(
           "access-control-request-headers",
         );
-        expect(
-          eventMock.response.headers.has("access-control-max-age"),
-        ).toEqual(false);
+        expect(eventMock.res.headers.has("access-control-max-age")).toEqual(
+          false,
+        );
       }
 
       {
@@ -506,23 +506,23 @@ describe("cors (unit)", () => {
         appendCorsPreflightHeaders(eventMock, options);
 
         expect(
-          eventMock.response.headers.get("access-control-allow-origin"),
+          eventMock.res.headers.get("access-control-allow-origin"),
         ).toEqual("*");
         expect(
-          eventMock.response.headers.has("access-control-allow-credentials"),
+          eventMock.res.headers.has("access-control-allow-credentials"),
         ).toEqual(false);
         expect(
-          eventMock.response.headers.has("access-control-allow-methods"),
+          eventMock.res.headers.has("access-control-allow-methods"),
         ).toEqual(false);
         expect(
-          eventMock.response.headers.get("access-control-allow-headers"),
+          eventMock.res.headers.get("access-control-allow-headers"),
         ).toEqual("CUSTOM-HEADER");
-        expect(eventMock.response.headers.get("vary")).toEqual(
+        expect(eventMock.res.headers.get("vary")).toEqual(
           "access-control-request-headers",
         );
-        expect(
-          eventMock.response.headers.get("access-control-max-age"),
-        ).toEqual("12345");
+        expect(eventMock.res.headers.get("access-control-max-age")).toEqual(
+          "12345",
+        );
       }
 
       {
@@ -542,21 +542,21 @@ describe("cors (unit)", () => {
         appendCorsPreflightHeaders(eventMock, options);
 
         expect(
-          eventMock.response.headers.get("access-control-allow-origin"),
+          eventMock.res.headers.get("access-control-allow-origin"),
         ).toEqual("https://example.com");
-        expect(eventMock.response.headers.get("vary")).toEqual("origin");
+        expect(eventMock.res.headers.get("vary")).toEqual("origin");
         expect(
-          eventMock.response.headers.get("access-control-allow-credentials"),
+          eventMock.res.headers.get("access-control-allow-credentials"),
         ).toEqual("true");
         expect(
-          eventMock.response.headers.has("access-control-allow-methods"),
-        ).toEqual(false);
-        expect(
-          eventMock.response.headers.has("access-control-allow-headers"),
+          eventMock.res.headers.has("access-control-allow-methods"),
         ).toEqual(false);
         expect(
-          eventMock.response.headers.has("access-control-max-age"),
+          eventMock.res.headers.has("access-control-allow-headers"),
         ).toEqual(false);
+        expect(eventMock.res.headers.has("access-control-max-age")).toEqual(
+          false,
+        );
       }
     });
   });
@@ -587,13 +587,13 @@ describe("cors (unit)", () => {
         appendCorsHeaders(eventMock, options);
 
         expect(
-          eventMock.response.headers.get("access-control-allow-origin"),
+          eventMock.res.headers.get("access-control-allow-origin"),
         ).toEqual("*");
         expect(
-          eventMock.response.headers.has("access-control-allow-credentials"),
+          eventMock.res.headers.has("access-control-allow-credentials"),
         ).toEqual(false);
         expect(
-          eventMock.response.headers.get("access-control-expose-headers"),
+          eventMock.res.headers.get("access-control-expose-headers"),
         ).toEqual("*");
       }
 
@@ -614,13 +614,13 @@ describe("cors (unit)", () => {
         appendCorsHeaders(eventMock, options);
 
         expect(
-          eventMock.response.headers.get("access-control-allow-origin"),
+          eventMock.res.headers.get("access-control-allow-origin"),
         ).toEqual("*");
         expect(
-          eventMock.response.headers.has("access-control-allow-credentials"),
+          eventMock.res.headers.has("access-control-allow-credentials"),
         ).toEqual(false);
         expect(
-          eventMock.response.headers.get("access-control-expose-headers"),
+          eventMock.res.headers.get("access-control-expose-headers"),
         ).toEqual("EXPOSE-HEADER,Authorization");
       }
 
@@ -640,11 +640,11 @@ describe("cors (unit)", () => {
         appendCorsHeaders(eventMock, options);
 
         expect(
-          eventMock.response.headers.get("access-control-allow-origin"),
+          eventMock.res.headers.get("access-control-allow-origin"),
         ).toEqual("https://example.com");
-        expect(eventMock.response.headers.get("vary")).toEqual("origin");
+        expect(eventMock.res.headers.get("vary")).toEqual("origin");
         expect(
-          eventMock.response.headers.get("access-control-allow-credentials"),
+          eventMock.res.headers.get("access-control-allow-credentials"),
         ).toEqual("true");
       }
     });
diff --git a/test/utils.test.ts b/test/utils.test.ts
index ab2c8ef..bcb923c 100644
--- a/test/utils.test.ts
+++ b/test/utils.test.ts
@@ -60,7 +60,7 @@ describeMatrix("utils", (t, { it, describe, expect }) => {
 
   describe("getMethod", () => {
     it("can get method", async () => {
-      t.app.all("/*", (event) => event.request.method);
+      t.app.all("/*", (event) => event.req.method);
       expect(await (await t.fetch("/api")).text()).toBe("GET");
       expect(await (await t.fetch("/api", { method: "POST" })).text()).toBe(
         "POST",
@@ -224,14 +224,14 @@ describeMatrix("utils", (t, { it, describe, expect }) => {
         getRequestFingerprint(event, {
           hash: false,
           ip: false,
-          path: true,
+          url: true,
           method: true,
         }),
       );
 
       const res = await t.fetch("/foo", { method: "POST" });
 
-      expect(await res.text()).toBe("POST|/foo");
+      expect(await res.text()).toMatch(/^POST\|http.+\/foo$/);
     });
 
     it("uses user agent when available", async () => {