{"id":16211,"library":"servie","title":"Servie HTTP Interfaces","description":"Servie provides a standard, framework-agnostic set of HTTP interfaces, including `Request`, `Response`, `Body`, `Headers`, and `AbortController`, designed for building interchangeable HTTP clients and servers in both Node.js and browser environments. It aims to offer primitives similar to the Web Fetch API, facilitating universal JavaScript HTTP operations. The current stable version is `4.3.3`, with minor releases frequently addressing bug fixes and introducing small features. Its key differentiators include its dual-environment compatibility without configuration, its focus on raw HTTP interface primitives rather than a full framework, and its modular design which allows it to be used as a foundational layer for various transport mechanisms and middleware libraries.","status":"active","version":"4.3.3","language":"javascript","source_language":"en","source_url":"git://github.com/serviejs/servie","tags":["javascript","request","response","http","interface","server","browser","req","res","typescript"],"install":[{"cmd":"npm install servie","lang":"bash","label":"npm"},{"cmd":"yarn add servie","lang":"bash","label":"yarn"},{"cmd":"pnpm add servie","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"Servie primarily uses named exports. For Node.js-only environments in TypeScript, consider `import { Request } from 'servie/dist/node'` to avoid DOM type conflicts.","wrong":"const Request = require('servie').Request","symbol":"Request","correct":"import { Request } from 'servie'"},{"note":"Servie's core types are named exports. Ensure you use destructuring for imports. In a pure browser context, you can also use `import { Response } from 'servie/dist/browser'`.","wrong":"import Response from 'servie'","symbol":"Response","correct":"import { Response } from 'servie'"},{"note":"The `Body` class is the base for `Request` and `Response`. CommonJS `require` is not recommended for Servie in modern applications.","wrong":"const Body = require('servie')","symbol":"Body","correct":"import { Body } from 'servie'"}],"quickstart":{"code":"import { Request, Response, Body, Headers, AbortController } from \"servie\";\n\n// A simple Servie-compatible request handler\nasync function handleRequest(request: Request): Promise<Response> {\n  const url = new URL(request.url);\n  console.log(`Received request for: ${url.pathname} with method ${request.method}`);\n\n  if (url.pathname === \"/greet\" && request.method === \"POST\") {\n    const name = await request.text();\n    const responseBody = `Hello, ${name}!`;\n    const headers = new Headers({ \"Content-Type\": \"text/plain\" });\n    return new Response(responseBody, { status: 200, headers });\n  }\n\n  if (url.pathname === \"/json\" && request.method === \"GET\") {\n    const data = { message: \"This is a JSON response\", timestamp: new Date().toISOString() };\n    const headers = new Headers({ \"Content-Type\": \"application/json\" });\n    return new Response(JSON.stringify(data), { status: 200, headers });\n  }\n\n  if (url.pathname === \"/abort-test\") {\n    const controller = new AbortController();\n    const signal = controller.signal;\n    setTimeout(() => controller.abort(), 100); // Abort after 100ms\n    try {\n      await new Promise((resolve, reject) => {\n        signal.addEventListener('abort', () => reject(new Error('Request aborted')));\n        // Simulate a long operation that could be aborted\n        setTimeout(resolve, 500);\n      });\n      return new Response(\"Operation completed before abort.\", { status: 200 });\n    } catch (e: any) {\n      if (e.message === 'Request aborted') {\n        return new Response(\"Operation aborted successfully.\", { status: 400 });\n      }\n      throw e;\n    }\n  }\n\n  return new Response(\"Not Found\", { status: 404 });\n}\n\n// Example usage:\nasync function runExamples() {\n  // 1. Simple GET request\n  const getRequest = new Request(\"http://localhost:3000/json\", { method: \"GET\" });\n  const getResponse = await handleRequest(getRequest);\n  console.log(`GET /json Status: ${getResponse.status}, Body: ${await getResponse.json().then(data => JSON.stringify(data))}`);\n\n  // 2. POST request with a text body\n  const postRequest = new Request(\"http://localhost:3000/greet\", {\n    method: \"POST\",\n    body: \"World\",\n    headers: { \"Content-Type\": \"text/plain\" }\n  });\n  const postResponse = await handleRequest(postRequest);\n  console.log(`POST /greet Status: ${postResponse.status}, Body: ${await postResponse.text()}`);\n\n  // 3. Aborted request demonstration\n  const abortRequest = new Request(\"http://localhost:3000/abort-test\", { method: \"GET\" });\n  const abortResponse = await handleRequest(abortRequest);\n  console.log(`GET /abort-test Status: ${abortResponse.status}, Body: ${await abortResponse.text()}`);\n}\n\nrunExamples().catch(console.error);","lang":"typescript","description":"This quickstart demonstrates how to create `Request` and `Response` objects, handle various body types (text, JSON), manage headers, and utilize `AbortController` for request cancellation within a simple Servie-compatible request handler."},"warnings":[{"fix":"Review existing signal handling logic; if signal forwarding was implicitly used, you may need to implement explicit signal propagation in your application code.","message":"Servie v4.0.10 removed signal forwarding code paths. If your application relied on Servie's internal mechanisms for forwarding `AbortSignal`s, this behavior will have changed or been removed.","severity":"breaking","affected_versions":">=4.0.10"},{"fix":"To avoid `dom` type conflicts in Node.js, import explicitly from `servie/dist/node` (e.g., `import { Request } from 'servie/dist/node'`). Similarly, for pure browser environments, use `servie/dist/browser`.","message":"When using Servie in a Node.js-only TypeScript project, importing from the main `servie` entry point can introduce `dom` types, leading to conflicts or requiring `skipLibCheck`. This is because the main entry point is universal.","severity":"gotcha","affected_versions":">=4.0.0"},{"fix":"When writing universal code that handles stream bodies, use environment-specific checks or abstractions (e.g., `servie/dist/node` or `servie/dist/browser` imports) to ensure compatibility with the correct stream type.","message":"The types supported for `Body` streams differ between Node.js and browser environments. Node.js supports `Readable` streams, while browsers support `ReadableStream`.","severity":"gotcha","affected_versions":">=4.0.0"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"If you need to read the body multiple times, call `.clone()` first: `const clonedResponse = response.clone(); const data1 = await clonedResponse.json(); const data2 = await response.text();`","cause":"The `Body` (and thus `Request` or `Response`) can only be consumed once. If you call `text()`, `json()`, or `arrayBuffer()` on a body, subsequent calls will fail.","error":"TypeError: response.json is not a function"},{"fix":"Change your import statements to use ESM syntax: `import { Request, Response } from 'servie';`","cause":"You are attempting to use CommonJS `require()` syntax in an ECMAScript Module (ESM) environment (e.g., `type: \"module\"` in `package.json` or a `.mjs` file).","error":"ReferenceError: require is not defined"},{"fix":"Explicitly import from the Node.js distribution: `import { Request, Response } from 'servie/dist/node';` Alternatively, configure your `tsconfig.json` to include appropriate `lib` entries or set `skipLibCheck: true`.","cause":"You are likely importing from `servie`'s main entry point in a Node.js TypeScript project, which includes DOM types globally, conflicting with a pure Node.js environment setup.","error":"Property 'fetch' does not exist on type 'Global' (or similar TypeScript DOM type conflict)"}],"ecosystem":"npm"}