Skip to content

Latest commit

 

History

History

node-fetch-server

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

node-fetch-server

node-fetch-server allows you to build servers for Node.js that use the web Fetch API primitives (namely Request and Response) instead of the traditional req/res API used in libraries like Express.

This web standard API is already used in many places across the JavaScript ecosystem:

When you write servers using the Request and Response APIs, you maximize the chances that your code will be portable across these different JavaScript runtimes.

Features

  • Use web standard Request and Response APIs for building servers, instead of node-specific API
  • Seamless integration with node:http and node:https modules
  • Supports custom hostnames (e.g. using process.env.HOST on a VPS to set the host portion of incoming request URLs)
  • Supports streaming responses using new Response(stream)
  • Exposes remote client address info

Installation

npm install @mjackson/node-fetch-server

Usage

import * as http from 'node:http';
import { createRequestListener } from '@mjackson/node-fetch-server';

function handler(request: Request) {
  return new Response('Hello, world!');
}

let server = http.createServer(createRequestListener(handler));

server.listen(3000);

By default request.url is derived from the value of the Host HTTP header and the connection protocol being used. To support custom hostnames using e.g. a HOST environment variable, you can use the host option:

import * as assert from 'node:assert/strict';
import * as http from 'node:http';
import { createRequestListener } from '@mjackson/node-fetch-server';

function handler(request: Request) {
  // This is now true
  assert.equal(new URL(request.url).host, process.env.HOST);
  return new Response('Hello, world!');
}

let server = http.createServer(createRequestListener(handler, { host: process.env.HOST }));

server.listen(3000);

Information about the remote client IP and port is passed as the 2nd argument to your FetchHandler:

import { type FetchHandler } from '@mjackson/node-fetch-server';

let handler: FetchHandler = (request, client) => {
  return new Response(`The client IP address is ${client.address}`);
};

Low-level API

In addition to the high-level createRequestListener() API, this package also provides 2 low-level APIs that are useful when building custom fetch-based servers in Node.js:

  • createRequest(req: http.IncomingMessage, res: http.ServerResponse, options: RequestOptions): Request
  • sendResponse(res: http.ServerResponse, response: Response): Promise<void>

These two functions serve as an efficient, minimal translation layer between Node.js req/res objects and fetch Request/Response objects. You could build your own custom server like this:

import * as http from 'node:http';
import { createRequest, sendResponse } from '@mjackson/node-fetch-server';

let server = http.createServer(async (req, res) => {
  let request = createRequest(req, res, { host: process.env.HOST });

  try {
    let response = await customAppLogic(request);
    await sendResponse(res, response);
  } catch (error) {
    console.error(error);
    res.writeHead(500, { 'Content-Type': 'text/plain' });
    res.end('Internal Server Error');
  }
});

server.listen(3000);

Related Packages

  • fetch-proxy - Build HTTP proxy servers using the web fetch API

Benchmark

A basic "hello world" benchmark shows node-fetch-server introduces considerable overhead on top of a vanilla node:http server. However, it is still able to serve more requests per second (and has higher overall throughput) than Express v4, so the slowdown should be acceptable for most applications.

> @mjackson/[email protected] bench /Users/michael/Projects/remix-the-web/packages/node-fetch-server
> bash ./bench/runner.sh

Platform: Darwin (24.0.0)
CPU: Apple M1 Pro
Date: 11/14/2024, 2:30:22 PM

Running benchmark for node:[email protected] ...

Running 30s test @ http://127.0.0.1:3000/
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     9.97ms   31.92ms 786.67ms   99.09%
    Req/Sec     4.45k   268.33     6.38k    93.69%
  1594257 requests in 30.02s, 326.89MB read
  Socket errors: connect 0, read 1317, write 6, timeout 0
Requests/sec:  53110.92
Transfer/sec:     10.89MB

Running benchmark for [email protected] ...

Running 30s test @ http://127.0.0.1:3000/
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    22.74ms   81.06ms   1.46s    98.22%
    Req/Sec     2.42k   185.82     4.30k    91.80%
  866347 requests in 30.03s, 177.64MB read
  Socket errors: connect 0, read 1496, write 3, timeout 0
Requests/sec:  28849.46
Transfer/sec:      5.92MB

Running benchmark for [email protected] ...

Running 30s test @ http://127.0.0.1:3000/
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    36.46ms  125.89ms   1.99s    97.89%
    Req/Sec     1.56k   146.86     2.93k    88.25%
  558504 requests in 30.06s, 134.76MB read
  Socket errors: connect 0, read 1261, write 11, timeout 36
Requests/sec:  18579.11
Transfer/sec:      4.48MB

License

See LICENSE