Skip to main content

node-fetch-server

Build Node.js servers with web-standard Fetch API primitives. node-fetch-server converts Node’s HTTP server interfaces into Request/Response flows that match modern runtimes.

Installation

npm i remix

Core Functions

createRequestListener

Wraps a fetch handler in a Node.js request listener that can be used with http.createServer(), https.createServer(), http2.createServer(), or http2.createSecureServer().
import * as http from 'node:http'
import { createRequestListener } from 'remix/node-fetch-server'

async function handler(request) {
  return new Response('Hello, world!')
}

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

server.listen(3000)
handler
FetchHandler
required
The fetch handler to use for processing incoming requests
options
RequestListenerOptions
Optional configuration options
return
http.RequestListener
A Node.js request listener function

createRequest

Creates a Request object from a Node.js http.IncomingMessage/http.ServerResponse pair or http2.Http2ServerRequest/http2.Http2ServerResponse pair.
import * as http from 'node:http'
import { createRequest, sendResponse } from 'remix/node-fetch-server'

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

server.listen(3000)
req
http.IncomingMessage | http2.Http2ServerRequest
required
The incoming request object
res
http.ServerResponse | http2.Http2ServerResponse
required
The server response object
options
RequestOptions
Options for creating the request
return
Request
A web-standard Request object

createHeaders

Creates a Headers object from the headers in a Node.js http.IncomingMessage or http2.Http2ServerRequest.
import { createHeaders } from 'remix/node-fetch-server'

let headers = createHeaders(req)
console.log(headers.get('Content-Type'))
req
http.IncomingMessage | http2.Http2ServerRequest
required
The incoming request object
return
Headers
A web-standard Headers object

sendResponse

Sends a Response to the client using a Node.js http.ServerResponse or http2.Http2ServerResponse object.
import { sendResponse } from 'remix/node-fetch-server'

await sendResponse(res, new Response('Hello, world!'))
res
http.ServerResponse | http2.Http2ServerResponse
required
The server response object
response
Response
required
The response to send
return
Promise<void>
A promise that resolves when the response has been sent

Types

FetchHandler

A function that handles an incoming request and returns a response.
interface FetchHandler {
  (request: Request, client: ClientAddress): Response | Promise<Response>
}
request
Request
required
The incoming request
client
ClientAddress
required
Information about the client that sent the request
return
Response | Promise<Response>
A response to send to the client

ClientAddress

Information about the client that sent a request.
interface ClientAddress {
  address: string
  family: 'IPv4' | 'IPv6'
  port: number
}
address
string
The IP address of the client that sent the request
family
'IPv4' | 'IPv6'
The family of the client IP address
port
number
The remote port of the client that sent the request

ErrorHandler

A function that handles an error that occurred during request handling. May return a response to send to the client, or undefined to allow the server to send a default error response.
interface ErrorHandler {
  (error: unknown): void | Response | Promise<void | Response>
}
error
unknown
required
The error that was thrown
return
void | Response | Promise<void | Response>
A response to send to the client, or undefined for the default error response

Examples

Basic Server

import * as http from 'node:http'
import { createRequestListener } from 'remix/node-fetch-server'

let users = new Map([
  ['1', { id: '1', name: 'Alice', email: '[email protected]' }],
  ['2', { id: '2', name: 'Bob', email: '[email protected]' }],
])

async function handler(request: Request) {
  let url = new URL(request.url)

  if (url.pathname === '/' && request.method === 'GET') {
    return new Response('Welcome to the User API!')
  }

  if (url.pathname === '/api/users' && request.method === 'GET') {
    return Response.json(Array.from(users.values()))
  }

  let userMatch = url.pathname.match(/^\/api\/users\/(\w+)$/)
  if (userMatch && request.method === 'GET') {
    let user = users.get(userMatch[1])
    if (user) {
      return Response.json(user)
    }
    return new Response('User not found', { status: 404 })
  }

  return new Response('Not Found', { status: 404 })
}

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

server.listen(3000, () => {
  console.log('Server running at http://localhost:3000')
})

Accessing Client Information

import { type FetchHandler } from 'remix/node-fetch-server'

let handler: FetchHandler = async (request, client) => {
  console.log(`Request from ${client.address}:${client.port}`)

  if (isRateLimited(client.address)) {
    return new Response('Too Many Requests', { status: 429 })
  }

  return Response.json({
    message: 'Hello!',
    yourIp: client.address,
  })
}

Streaming Responses

async function handler(request: Request) {
  if (request.url.endsWith('/stream')) {
    let stream = new ReadableStream({
      async start(controller) {
        for (let i = 0; i < 5; i++) {
          controller.enqueue(new TextEncoder().encode(`Chunk ${i}\n`))
          await new Promise((resolve) => setTimeout(resolve, 1000))
        }
        controller.close()
      },
    })

    return new Response(stream, {
      headers: { 'Content-Type': 'text/plain' },
    })
  }

  return new Response('Not Found', { status: 404 })
}

HTTPS Support

import * as https from 'node:https'
import * as fs from 'node:fs'
import { createRequestListener } from 'remix/node-fetch-server'

let options = {
  key: fs.readFileSync('private-key.pem'),
  cert: fs.readFileSync('certificate.pem'),
}

let server = https.createServer(options, createRequestListener(handler))

server.listen(443, () => {
  console.log('HTTPS Server running on port 443')
})

Custom Error Handling

import { createRequestListener } from 'remix/node-fetch-server'

async function errorHandler(error: unknown): Promise<Response> {
  console.error('Server error:', error)
  
  if (error instanceof Error && error.message === 'Unauthorized') {
    return new Response('Unauthorized', { status: 401 })
  }
  
  return new Response('Internal Server Error', { status: 500 })
}

let server = http.createServer(
  createRequestListener(handler, { onError: errorHandler })
)

Build docs developers (and LLMs) love