{"_id":"@remix-run/node-serve","_rev":"3-464c88207a0758127c91fbd188e150f8","name":"@remix-run/node-serve","dist-tags":{"latest":"0.2.0"},"versions":{"0.0.0":{"name":"@remix-run/node-serve","version":"0.0.0","license":"MIT","_id":"@remix-run/node-serve@0.0.0","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"homepage":"https://github.com/remix-run/remix#readme","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"a932e5955e1b0620713a0dfab521fb772038b115","tarball":"https://registry.npmjs.org/@remix-run/node-serve/-/node-serve-0.0.0.tgz","fileCount":2,"integrity":"sha512-iqVP/VA/9NI0aSTSPbkyHg03XvhY6AXuayXiMKnQkIt8YpOdvplYwHSVNSu52UapvwNZVY56l6uz9M5KffOQTA==","signatures":[{"sig":"MEUCIQDu5+fcHpVookHhdIEmEL6HRpTVkl0vMz+S4vo77NuQtgIgdXIUh9XbsSr/QKMnODHUoR+loFVMaK7pB2KacWDASQ4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":467},"_npmUser":{"name":"mjackson","email":"mjijackson+npm@gmail.com"},"repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/node-serve"},"_npmVersion":"11.12.1","description":"Placeholder package for Remix CI/OIDC setup","directories":{},"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/node-serve_0.0.0_1777451398536_0.07127287061112297","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@remix-run/node-serve","version":"0.1.0","keywords":["http","server","fetch","web","node","performance"],"author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"license":"MIT","_id":"@remix-run/node-serve@0.1.0","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"homepage":"https://github.com/remix-run/remix/tree/main/packages/node-serve#readme","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"93733b3b49a2f46c6427e325e544b2b2f9b7a3aa","tarball":"https://registry.npmjs.org/@remix-run/node-serve/-/node-serve-0.1.0.tgz","fileCount":23,"integrity":"sha512-/OhPBuJkEswC7og5yHc5kG2JnOBHwWCXP4051X05K73Y7e7iAzcgjGz8eYaKNR5Lvk2uUmcECNelRdqNRxFSbg==","signatures":[{"sig":"MEUCIA/4j165zxBBTGZpUf2+mEh/zqNFvYmaLlKdjcjkl1dWAiEA/Bm0T0FL1FRWqlZuViiMzNsryeVB/LSPyjyvEOUdiQU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@remix-run%2fnode-serve@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":59705},"type":"module","_from":"file:remix-run-node-serve-0.1.0.tgz","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"test":"remix-test","build":"tsgo -p tsconfig.build.json","clean":"git clean -fdX","typecheck":"tsgo --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:db93c673-4357-4693-8b39-ceb001207b5a"}},"_resolved":"/tmp/b2846c22ad9646bcadf617bc10b55274/remix-run-node-serve-0.1.0.tgz","_integrity":"sha512-/OhPBuJkEswC7og5yHc5kG2JnOBHwWCXP4051X05K73Y7e7iAzcgjGz8eYaKNR5Lvk2uUmcECNelRdqNRxFSbg==","repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/node-serve"},"_npmVersion":"11.11.0","description":"Build high-performance Fetch API servers for Node.js","directories":{},"_nodeVersion":"24.14.1","_hasShrinkwrap":false,"devDependencies":{"@types/node":"^24.6.0","@remix-run/test":"0.3.0","@remix-run/assert":"0.2.0","@typescript/native-preview":"7.0.0-dev.20251125.1"},"optionalDependencies":{"uWebSockets.js":"github:uNetworking/uWebSockets.js#v20.66.0"},"_npmOperationalInternal":{"tmp":"tmp/node-serve_0.1.0_1777576258808_0.46056574118140436","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@remix-run/node-serve","version":"0.2.0","description":"Build high-performance Fetch API servers for Node.js","author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"repository":{"type":"git","url":"git+https://github.com/remix-run/remix.git","directory":"packages/node-serve"},"homepage":"https://github.com/remix-run/remix/tree/main/packages/node-serve#readme","license":"MIT","type":"module","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"devDependencies":{"@types/node":"^24.6.0","@typescript/native-preview":"7.0.0-dev.20251125.1","@remix-run/assert":"0.2.0","@remix-run/test":"0.4.0"},"dependencies":{"uWebSockets.js":"github:uNetworking/uWebSockets.js#v20.66.0"},"keywords":["http","server","fetch","web","node","performance"],"scripts":{"build":"tsgo -p tsconfig.build.json","clean":"git clean -fdX","test":"remix-test","typecheck":"tsgo --noEmit"},"_id":"@remix-run/node-serve@0.2.0","bugs":{"url":"https://github.com/remix-run/remix/issues"},"_integrity":"sha512-Pwc2QceR4nZT3UOKbhbqi0qOfZCeFZ2+pz5n3kmMOfe5VmtLYY5Wb6vT6750xr/iIP1eVr5yfSqd/jRUZKkxgA==","_resolved":"/tmp/031cadc42befed59d1db9c093462e5f3/remix-run-node-serve-0.2.0.tgz","_from":"file:remix-run-node-serve-0.2.0.tgz","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-Pwc2QceR4nZT3UOKbhbqi0qOfZCeFZ2+pz5n3kmMOfe5VmtLYY5Wb6vT6750xr/iIP1eVr5yfSqd/jRUZKkxgA==","shasum":"3eb2ce7ce7302063c7a38a1753eb71cdf4875a38","tarball":"https://registry.npmjs.org/@remix-run/node-serve/-/node-serve-0.2.0.tgz","fileCount":23,"unpackedSize":56995,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@remix-run%2fnode-serve@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEdRGY5NpQsdMwoS9+WSUD1jE05sPLqBtArdvgV+8wyNAiEA3y4ozkMBl+zV/IdZK8kjMUqkSoAaO4+BgOReJqNs4RY="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:db93c673-4357-4693-8b39-ceb001207b5a"}},"directories":{},"maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/node-serve_0.2.0_1779247252563_0.2641616831592406"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-29T08:29:58.437Z","modified":"2026-05-20T03:20:53.027Z","0.0.0":"2026-04-29T08:29:58.674Z","0.1.0":"2026-04-30T19:10:58.957Z","0.2.0":"2026-05-20T03:20:52.726Z"},"bugs":{"url":"https://github.com/remix-run/remix/issues"},"author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"license":"MIT","homepage":"https://github.com/remix-run/remix/tree/main/packages/node-serve#readme","keywords":["http","server","fetch","web","node","performance"],"repository":{"type":"git","url":"git+https://github.com/remix-run/remix.git","directory":"packages/node-serve"},"description":"Build high-performance Fetch API servers for Node.js","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"readme":"# node-serve\n\nBuild high-performance Node.js servers with web-standard Fetch API primitives. Use this package when you want Remix-style `Request`/`Response` handlers with a managed server optimized for production throughput.\n\n## Features\n\n- **Fetch API Handlers**: Serve standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) to [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) request handlers\n- **High-Performance Node.js Server**: Start a fast managed server for Fetch API application code\n- **HTTPS Support**: Start a TLS server with certificate and key file paths\n- **Managed Server Lifecycle**: Start a server with `serve()`, wait for `server.ready`, and close it with `server.close()`\n- **Custom Hostname**: Override the host and protocol used to construct incoming `request.url` values\n- **Client Info**: Access client IP address, address family, and remote port when your handler accepts a second argument\n- **uWebSockets.js Setup**: Register native WebSocket routes and connection filters before the Fetch fallback starts listening\n- **Existing uWebSockets.js App Adapter**: Use `createUwsRequestHandler()` when you already own a uWebSockets.js app\n\n## Installation\n\n```sh\nnpm i remix\n```\n\n`node-serve` includes uWebSockets.js as its native high-performance transport.\n\n## Usage\n\nUse `serve()` to start a Node.js server that calls your fetch handler for every incoming request:\n\n```ts\nimport { serve } from 'remix/node-serve'\n\nlet users = new Map([\n  ['1', { id: '1', name: 'Alice', email: 'alice@example.com' }],\n  ['2', { id: '2', name: 'Bob', email: 'bob@example.com' }],\n])\n\nasync function handler(request: Request) {\n  let url = new URL(request.url)\n\n  if (url.pathname === '/' && request.method === 'GET') {\n    return new Response('Welcome to the User API! Try GET /api/users')\n  }\n\n  if (url.pathname === '/api/users' && request.method === 'GET') {\n    return Response.json(Array.from(users.values()))\n  }\n\n  let userMatch = url.pathname.match(/^\\/api\\/users\\/(\\w+)$/)\n  if (userMatch && request.method === 'GET') {\n    let user = users.get(userMatch[1])\n    if (user) return Response.json(user)\n    return new Response('User not found', { status: 404 })\n  }\n\n  return new Response('Not Found', { status: 404 })\n}\n\nlet server = serve(handler, { port: 3000 })\n\nawait server.ready\nconsole.log(`Server running at http://localhost:${server.port}`)\n```\n\n### uWebSockets.js Setup\n\nUse `setup(app)` when `serve()` should still manage the server lifecycle and Fetch fallback, but you need to configure native uWebSockets.js transport features before the server starts listening. The hook runs after the app is created and before `node-serve` registers its catch-all Fetch route.\n\nThis example adds a native WebSocket endpoint next to a normal Fetch handler on the same port:\n\n```ts\nimport { serve } from 'remix/node-serve'\n\nlet server = serve(\n  (request) => {\n    let url = new URL(request.url)\n\n    if (url.pathname === '/') {\n      return new Response('Chat server')\n    }\n\n    return new Response('Not Found', { status: 404 })\n  },\n  {\n    port: 3000,\n    setup(app) {\n      app.ws('/ws/chat', {\n        open(ws) {\n          ws.subscribe('chat')\n        },\n        message(ws, message, isBinary) {\n          ws.publish('chat', message, isBinary)\n        },\n      })\n    },\n  },\n)\n\nawait server.ready\n```\n\n`app.ws(pattern, ...)` uses uWebSockets.js route patterns, not Remix `route-pattern` syntax. uWS supports simple path params such as `/rooms/:room`, and those params are available during `upgrade` through `req.getParameter('room')` or `req.getParameter(0)`. If WebSocket handlers need params, copy them into uWS user data during `upgrade`, then read them with `ws.getUserData()`:\n\n```ts\nserve(handler, {\n  setup(app) {\n    app.ws<{ room: string }>('/rooms/:room', {\n      upgrade(res, req, context) {\n        res.upgrade(\n          { room: req.getParameter('room') ?? 'general' },\n          req.getHeader('sec-websocket-key'),\n          req.getHeader('sec-websocket-protocol'),\n          req.getHeader('sec-websocket-extensions'),\n          context,\n        )\n      },\n      open(ws) {\n        ws.subscribe(`room:${ws.getUserData().room}`)\n      },\n    })\n  },\n})\n```\n\nuWS patterns do not support the full `route-pattern` feature set, including partial-segment params like `v:version`, split params like `:file.:ext`, optionals like `/api(/v:version)`, host/protocol/search matching, or named multi-segment wildcard captures like `*path`. You can use a uWS pattern like `/files/*` as a catch-all, but it does not provide Remix-style named wildcard params.\n\nYou can also register connection filters for low-level transport metrics or limits:\n\n```ts\nlet activeConnections = 0\n\nserve(handler, {\n  setup(app) {\n    app.filter((_res, count) => {\n      activeConnections = Number(count)\n    })\n  },\n})\n```\n\n`app.filter()` observes low-level connection count changes. It is not Fetch response middleware and should not be used to mutate normal `Response` headers or bodies. Use Fetch handlers, middleware, or [`fetch-router`](https://github.com/remix-run/remix/tree/main/packages/fetch-router) for normal HTTP routing and response behavior; reserve `setup()` for uWebSockets.js transport features that must be configured before listening.\n\n### Custom Request URLs\n\nUse `host` and `protocol` when your server runs behind a proxy or load balancer and you need stable incoming request URLs:\n\n```ts\nimport { serve } from 'remix/node-serve'\n\nlet server = serve(handler, {\n  host: process.env.HOST ?? 'api.example.com',\n  protocol: 'https:',\n  port: 3000,\n})\n\nawait server.ready\n```\n\n### HTTPS\n\nPass `tls` options to start an HTTPS server. `keyFile` and `certFile` are file paths, not PEM contents:\n\n```ts\nimport { serve } from 'remix/node-serve'\n\nlet server = serve(handler, {\n  port: 443,\n  tls: {\n    keyFile: './certs/server.key',\n    certFile: './certs/server.crt',\n  },\n})\n\nawait server.ready\nconsole.log(`Server running at https://localhost:${server.port}`)\n```\n\nWhen `tls` is present, `request.url` defaults to the `https:` protocol. You can still set `protocol` explicitly when the public URL differs from the local server transport.\n\n### Client Information\n\nHandlers that accept a second argument receive the remote client address:\n\n```ts\nimport { type FetchHandler, serve } from 'remix/node-serve'\n\nlet handler: FetchHandler = async (request, client) => {\n  console.log(`Request from ${client.address}:${client.port}`)\n\n  return Response.json({\n    path: new URL(request.url).pathname,\n    clientAddress: client.address,\n  })\n}\n\nserve(handler, { port: 3000 })\n```\n\n### Existing uWebSockets.js Apps\n\nMost apps should use `serve()`. Use `createUwsRequestHandler()` when you already have a uWebSockets.js app and want only part of the app to use a Fetch API handler:\n\nThis example assumes `uWebSockets.js` is also a direct dependency of your app.\n\n```ts\nimport { App } from 'uWebSockets.js'\nimport { createUwsRequestHandler } from 'remix/node-serve'\n\nlet app = App()\n\nasync function handler(request: Request) {\n  let url = new URL(request.url)\n  return Response.json({ path: url.pathname })\n}\n\napp.get('/health', (res) => {\n  res.end('ok')\n})\n\napp.any('/api/*', createUwsRequestHandler(handler))\n\napp.listen(3000, (socket) => {\n  if (!socket) throw new Error('Could not listen on port 3000')\n})\n```\n\nFor HTTPS with an existing uWebSockets.js app, create the SSL app yourself and pass `protocol: 'https:'` when you create the request handler:\n\n```ts\nimport { SSLApp } from 'uWebSockets.js'\nimport { createUwsRequestHandler } from 'remix/node-serve'\n\nlet app = SSLApp({\n  key_file_name: './certs/server.key',\n  cert_file_name: './certs/server.crt',\n})\n\napp.any('/*', createUwsRequestHandler(handler, { protocol: 'https:' }))\napp.listen(443, (socket) => {\n  if (!socket) throw new Error('Could not listen on port 443')\n})\n```\n\n## Related Packages\n\n- [`node-fetch-server`](https://github.com/remix-run/remix/tree/main/packages/node-fetch-server) - Adapt Fetch API handlers to existing `node:http`, `node:https`, and `node:http2` servers\n- [`fetch-router`](https://github.com/remix-run/remix/tree/main/packages/fetch-router) - Route incoming `Request` objects to Fetch API handlers\n\n## Related Work\n\n- [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) - Web standard `Request` and `Response` primitives\n\n## License\n\nSee [LICENSE](https://github.com/remix-run/remix/blob/main/LICENSE)\n","readmeFilename":"README.md"}