node-fetch-server Overview
The node-fetch-server package bridges two worlds: the standard Fetch API (used by Remix) and Node.js HTTP servers. It converts a function that takes a Request and returns a Response into something Node.js can use with http.createServer().
Why Does This Package Exist?
Remix is built on web standards. Every handler works with Request and Response -- the same types available in browsers, Cloudflare Workers, Deno, and Bun. But Node.js has its own, older HTTP API (http.IncomingMessage and http.ServerResponse) that predates the Fetch API.
This package solves that mismatch. It handles the conversion so you can write standard Fetch API code and run it on Node.js without thinking about Node-specific details.
Browser/Client
|
| HTTP request
v
Node.js http.createServer()
|
| node-fetch-server converts to Request
v
Your Fetch handler (Request -> Response)
|
| node-fetch-server converts to Node.js response
v
Browser/Client receives the responseWhat is the Fetch API?
The Fetch API is a web standard for making and handling HTTP requests. It defines Request, Response, Headers, and URL objects. Originally designed for browsers, these types are now available in most JavaScript runtimes. Writing code against the Fetch API means your code is portable across platforms.
Key Features
Simple API
One function does everything you need:
import * as http from 'node:http'
import { createRequestListener } from 'remix/node-fetch-server'
async function handler(request: Request): Promise<Response> {
return new Response('Hello from Remix!')
}
let server = http.createServer(createRequestListener(handler))
server.listen(3000)Works with the Remix Router
The most common usage is to pass a router's fetch method directly:
import { createRouter } from 'remix/fetch-router'
import { createRequestListener } from 'remix/node-fetch-server'
let router = createRouter()
router.get('/', () => new Response('Home'))
let server = http.createServer(createRequestListener(router.fetch))Streaming
Request and response bodies are streamed -- not buffered in memory. This means large file uploads and downloads work efficiently without consuming excessive memory.
HTTPS and HTTP/2
Works with https.createServer() and http2.createServer() in addition to plain HTTP. Protocols are detected automatically.
Client Address Information
Your handler receives the client's IP address, address family, and port number for logging, rate limiting, or access control.
Error Handling
A configurable error handler catches exceptions thrown by your fetch handler, preventing unhandled crashes and letting you return custom error responses.
Quick Example
Here is a complete Node.js server using the Fetch API:
import * as http from 'node:http'
import { createRequestListener } from 'remix/node-fetch-server'
async function handler(request: Request): Promise<Response> {
let url = new URL(request.url)
if (url.pathname === '/') {
return new Response('Welcome!')
}
if (url.pathname === '/json') {
return Response.json({ message: 'Hello', timestamp: Date.now() })
}
return new Response('Not Found', { status: 404 })
}
let server = http.createServer(
createRequestListener(handler, {
onError(error) {
console.error('Request failed:', error)
return new Response('Internal Server Error', { status: 500 })
},
})
)
server.listen(3000, () => {
console.log('Server running at http://localhost:3000')
})How it Fits in the Remix Ecosystem
- fetch-router creates the handler function;
node-fetch-serverruns it on Node.js. - fetch-proxy lets you forward requests to other servers, and you serve the proxy with
node-fetch-server.
If you deploy to a platform that already supports the Fetch API natively (Cloudflare Workers, Deno, Bun), you do not need this package at all -- the router's fetch method works directly.
Next Steps
- Tutorial: Set Up a Node.js Server -- Step-by-step guide from zero to a running server.
- API Reference -- Complete documentation of
createRequestListener,createRequest,sendResponse, and all options.