# Node.js Support

> Learn more about Node.js compatibility with srvx.

<note>

This is an advanced section, explaining internal mechanism of srvx for Node.js support.

</note>

When you want to create a HTTP server using [Node.js](https://nodejs.org/), you have to use [node:http](https://nodejs.org/api/http.html) builtin.

**Example:** Simple Node.js server.

```js
import { createServer } from "node:http";

createServer((req, res) => {
  res.end("Hello, Node.js!");
}).listen(3000);
```

Whenever a new request is received, the request event is called with two objects: a request `req` object ([node:IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage)) to access HTTP request details and a response `res` object ([node:ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse)) that can be used to prepare and send a HTTP response. Popular framework such as [Express](https://expressjs.com/) and [Fastify](https://fastify.dev/) are also based on Node.js server API.

Recent JavaScript server runtimes like [Deno](https://deno.com/) and [Bun](https://bun.sh/) have a different way to define a server which is similar to web [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API.

**Example:** [Deno](https://deno.com/) HTTP server ([learn more](https://docs.deno.com/api/deno/~/Deno.serve)):

```js
Deno.serve({ port: 3000 }, (_req, info) => new Response("Hello, Deno!"));
```

**Example:** [Bun](https://bun.sh/) HTTP server ([learn more](https://bun.sh/docs/api/http)):

```js
Bun.serve({ port: 3000, fetch: (req) => new Response("Hello, Bun!") });
```

As you probably noticed, there is a difference between [Node.js](https://nodejs.org/) and [Deno](https://deno.com/) and [Bun](https://bun.sh/). The incoming request is a web [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object and server response is a web [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object. Accessing headers, request path, and preparing response is completely different between [Node.js](https://nodejs.org/) and other runtimes.

While [Deno](https://deno.com/) and [Bun](https://bun.sh/) servers are both based on web standards, There are differences between them. The way to provide options, server lifecycle, access to request info such as client IP which is not part of [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) standard are some examples.

## How Node.js Compatibility Works

srvx internally uses a lightweight proxy system that wraps [node:IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) as [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) and converts the final state of [node:ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse) to a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object.

For each incoming request, instead of *fully cloning* [Node.js](https://nodejs.org/) request object with into a new [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) instance, srvx creates a proxy that *translates* all property access and method calls between two interfaces.

With this method, we add **minimum amount of overhead** and can **optimize** internal implementation to leverage most of the possibilities with [Node.js](https://nodejs.org/) native primitives. This method also has the advantage that there is **only one source of trust** ([Node.js](https://nodejs.org/) request instance) and any changes to each interface will reflect the other ([node:IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) <> [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request)), **maximizing compatibility**. srvx will **never patch of modify** the global [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) constructors, keeping runtime natives untouched.

Internally, the fetch wrapper looks like this:

```ts
async function nodeHandler(nodeReq: IncomingMessage, nodeRes: ServerResponse) {
  const request = new NodeRequest(nodeReq);
  const response = await server.fetch(request);
  await sendNodeResponse(nodeRes, response);
}
```

... `NodeRequest`, wraps [node:IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) as a standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) interface. <br />


... On first `request.body` access, it starts reading request body as a [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream). <br />


... `request.headers` is a proxy (`NodeReqHeadersProxy`) around `nodeReq.headers` providing a standard [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) interface. <br />


... When accessing `request.url` getter, it creates a full URL string (including protocol, hostname and path) from `nodeReq.url` and `nodeReq.headers` (host). <br />


... Other request APIs are also implemented similarly.

`sendNodeResponse`, handles the [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object returned from server fetch method.

... `status`, `statusText`, and `headers` will be set. <br />


... `set-cookie` header will be properly split (with [cookie-es](https://cookie-es.unjs.io)). <br />


... If response has body, it will be streamed to node response. <br />


... The promise will be resolved after the response is sent and callback called by Node.js. <br />



## `NodeRequest`

`NodeRequest` wraps Node.js [`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) and exposes a Web-standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) interface.
It also extends the global `Request` class provided by Node.js (via [undici](https://github.com/nodejs/undici)).

Due to undici’s internal implementation, some edge cases exist.
For example, calling `new Request(req)` may throw `Cannot read private member #state from an object whose class did not declare it`.

To avoid these issues, you can use `req.clone()` or `req._request` to access an undici instance of request.

Alternatively, you can patch the global `Request` class in Node.js.

```js
import { patchGlobalRequest } from "srvx/node";

// Fix compatibility issues with Node.js Request
patchGlobalRequest();
```

This ensures `Request` behaves correctly when working with Node.js request objects and prevents common interoperability problems.

Global patch is faster (since only initializes undici instance when cloning) but might introduce more unwanted behaviors.

## `FastResponse`

When initializing a new [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) in Node.js, a lot of extra internals have to be initialized including a [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) object for `response.body` and [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) for `response.headers` which adds significant overhead since Node.js response handling does not need them.

Until there will be native [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) handling support in Node.js http module, srvx provides a faster alternative `Response` class. You can use this instead to replace `Response` and improve performance.

```js
import { serve, FastResponse } from "srvx";

const server = serve({
  port: 3000,
  fetch() {
    return new FastResponse("Hello!");
  },
});

await server.ready();

console.log(`Server running at ${server.url}`);
```

You can locally run benchmarks by cloning [srvx repository](https://github.com/h3js/srvx) and running `npm run bench:node [--all]` script. Speedup in v22.8.0 was roughly **%94**!

## Fetching Node.js Handlers

srvx server converts a [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)-like [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) => [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) handler to [node:IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) => [node:ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse) handler that is compatible with Node.js built-in server.

If you want to instead convert a Node.js server handler (like [Express](https://expressjs.com/)) to a web fetch handler, you can use `fetchNodeHandler` or `toFetchHandler` utils from `srvx/node`.

<note>

These utils are meant to run in Node.js runtime.

</note>

```js [express.mjs]
import { fetchNodeHandler, toFetchHandler } from "srvx/node";

import express from "express";

const app = express().get("/", (req, res) => {
  res.send("Hello World!");
});

// Convert express app to a fetch handler
const fetchHandler = toFetchHandler(app);
const res = await fetchHandler(new Request("http://localhost/"));

// Directly fetch express app handler
const res = await fetchNodeHandler(app, new Request("http://localhost/"));

// 200 OK Hello World!
console.log(res.status, res.statusText, await res.text());
```
