{"id":15011246,"url":"https://github.com/do-/node-http-server-tools","last_synced_at":"2026-01-05T22:03:50.459Z","repository":{"id":255440053,"uuid":"850393621","full_name":"do-/node-http-server-tools","owner":"do-","description":"Some wrappers around the standard node:http API","archived":false,"fork":false,"pushed_at":"2024-09-11T13:40:32.000Z","size":226,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-02T11:31:51.629Z","etag":null,"topics":["http","node","request","response"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/do-.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-31T16:40:54.000Z","updated_at":"2024-09-11T13:44:21.000Z","dependencies_parsed_at":"2024-09-06T19:55:22.406Z","dependency_job_id":null,"html_url":"https://github.com/do-/node-http-server-tools","commit_stats":null,"previous_names":["do-/node-http-server-tools"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/do-%2Fnode-http-server-tools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/do-%2Fnode-http-server-tools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/do-%2Fnode-http-server-tools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/do-%2Fnode-http-server-tools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/do-","download_url":"https://codeload.github.com/do-/node-http-server-tools/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239011398,"owners_count":19567658,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http","node","request","response"],"created_at":"2024-09-24T19:39:51.165Z","updated_at":"2025-10-30T17:31:18.592Z","avatar_url":"https://github.com/do-.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![workflow](https://github.com/do-/node-http-server-tools/actions/workflows/main.yml/badge.svg)\n![Jest coverage](./badges/coverage-jest%20coverage.svg)\n\n`http-server-tools` is a thin wrapper around the standard [`'node:http'`](https://nodejs.org/api/http.html) library for developing Web services with as few external dependencies as possible, without resorting to a full blown middleware framework. Or to build such frameworks.\n\nThe main class here, `HttpRequestContext`, incapsulates the [`ClientRequest`](https://nodejs.org/api/http.html#class-httpclientrequest)/[`ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) pair and implements an OO API hiding some transport protocol details while keeping low level operation totally available. \n\n`HttpRequestContext` don't assume subclassing, but is highly configurable and is meant to be used together with companion objects carrying sets of options for its constructor. Such objects should be singletons representing local application's specifics as opposed to one-off multi purpose `HttpRequestContext` instances.\n\nTo illustrate `HttpRequestContext` API in action, a trivial embeddable file directory sharing Web server, [`HttpStaticSite`](https://github.com/do-/node-http-server-tools/wiki/HttpStaticSite), is included in the library.\n\n# Installation\n```sh\nnpm install http-server-tools\n```\n# Usage\n```js\nconst createError          = require ('http-errors')\nconst {HttpRequestContext} = require ('http-server-tools')\n\nasync function handle (response) {\n\n  const ctx = new HttpRequestContext (response, {\n\n//    parse       : str =\u003e JSON.parse (str),        // for .bodyParams\n//    stringify   : obj =\u003e JSON.stringify (obj),    // for .write ({...})\n//    createError : err =\u003e createError (500, err, {expose: false}),\n\n//    maxBodySize : 10 * 1024 * 1024,\n//    pathBase    : 0,\n//    pathMapping :       // e. g. ([type, id]) =\u003e ({type, id})\n//    keepBody    :       // e. g. function () {return this.path [0] === 'huge'}\n\n//    statusCode  : 200,\n//    charset     : 'utf-8',\n//    contentType :       // e. g. 'text/xml', 'application/soap+xml'\n\n  })\n\n  const {pathParams, searchParams} = ctx // from the URL\n  const {sessionId}                = ctx.cookieParams\n  \n  await ctx.readBody ()      // if the method is 'GET' or `keepBody` returns true, does nothing\n  const {bodyParams} = ctx   // see the `parse` option\n\n  try {\n    const result = await invokeMyBusinessMethod ({...pathParams,...searchParams, ...bodyParams}, sessionId)\n    ctx.setCookie ('sessionId', sessionId, {httpOnly: true})\n    await ctx.write (result)\n  }\n  catch (err) {\n    await ctx.writeError (err)\n  }\n\n}\n\nhttp.createServer ({...})\n  .on ('request', (_, response) =\u003e \n    handle (response)\n    .then (..., ...)\n  )\n  .listen ({...})\n```\nIn essence, in most cases, to serve a request, one should:\n* create an `HttpRequestContext` instance;\n* fetch the request completely with `await ctx.readBody ()`;\n* calculate the result based on `{...pathParams, ...searchParams, ...bodyParams}` and, probably, `cookieParams`;\n* `ctx.write ()` it out.\n\n# Reading Incoming Data\nIn most cases, to acquire the totality of data sent from the client, the boilerplate code\n```js\n  await ctx.readBody ()\n  const {pathParams, searchParams, bodyParams, cookieParams: {sessionId}} = ctx \n  // now do something with {...pathParams, ...searchParams, ...bodyParams} and sessionId\n```\nshould fit right. Details are explained below in this section.\n\n## `.searchParams`\nThis property's value is an object composed from [url.searchParams](https://nodejs.org/api/url.html#urlsearchparams). For `/?type=users\u0026id=1`, it's `{type: 'users', 'id': '1'}`.\n\n## `.path`\nThis property presents the [`url.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/URL/pathname), split by `'/'`, with all empty strings filtered away, with first `pathBase` stripped off. For example, for `http://127.0.0.1/api/v1.0//users/1/?show=1#help` it will be \n\n|`pathBase`|`path`|\n| - | - |\n|`0` (default)|`['api', 'v1.0', 'users', '1']`|\n|`1`|`['v1.0', 'users', '1']`|\n|`2`|`['users', '1']`|\n\n## `.pathParams`\nIn many Web applications, components of the `path` with fixed positions have a clear business sense: for example, the root part means entity (name it `type`) and the second is the unique identifier (name it `id`): `/{type}/{id}`.\n\nIn such cases, `HttpRequestContext` lets configure a mapping function\n```js\n  pathMapping: ([type, id]) =\u003e ({type, id})\n```\nand obtain the corresponding named parameters, in the form of a plain Object, via the `.pathParams` property, similar to `.searchParams`.\n\nWithout `pathMapping` defined (which is the default), `.pathParams` is always an empty object `{}`.\n\n## `.body`\nInitially undefined, after `await ctx.readBody ()` this property becomes a [Buffer](https://nodejs.org/api/buffer.html#buffer) containing the whole request body.\n\nIf the [HTTP Request Method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) doesn't assume sending any body (`GET`, `HEAD` etc.), `.body` remains undefined.\n\nThe same happens for `'POST'`, `'PUT'` etc. when `.keepBody ()` returns `true`. By default, it never does but the developer might opt to alter it to read the `.request` stream explicitly. For example:\n```js\n  keepBody: function () {return this.searchParams.type === 'special'},\n```\nwill keep `.request` unread for `/type=special` even after `await ctx.readBody ()` is done.\n\nIf the `maxBodySize` (10 Mb by default) limit gets exceeded, a [413 Content Too Large](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413) [http error](https://www.npmjs.com/package/http-errors) is thrown.\n\nOverall, it's always safe to call `await ctx.readBody ()`, once per `ctx` instance.\n\n## `.bodyText`\nThis computed property returns the `.body` as a string, decoded with [`Content-Type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type)'s `charset`, 'utf-8' by default.\n\nException: for an undefined `.body`, `.bodyText` returns the zero length string: `''`.\n\n## `.bodyParams`\nThis computed property returns the result of applying the `parse` option (`JSON.parse` by default) to `.bodyText`.\n\nException: for a zero length `.bodyText`, `.bodyParams` returns the empty object: `{}`.\n\nSo, for instance, `{...searchParams, ...bodyParams}` is OK for `GET` requests without additional checks.\n\nFor SOAP and other XML based services, it takes to redefine the `parse` option using something like [XMLParser](https://github.com/do-/node-xml-toolkit/wiki/XMLParser).\n\n## `.cookieParams`\nThis property's getter is the shortcut to [cookie.parse ()](https://www.npmjs.com/package/cookie#cookieparsestr-options).\n\n# Writing Results\nIn short, to send business `data` (normally, a plain object or, in special cases, a stream) to the client, the application should call\n```js\nawait ctx.write (data)\n```\nonce per HTTP request. This high level all purpose method actually just checks for its argument type and forwards it to one of the specific methods described hereafter.\n\nAny of `write...` methods is presumed to be called once per request lifecycle, at its end. All custom HTTP headers (including `Set-Cookie`, see below) must be already set at this point.\n \nThe response status code is set from `ctx.statusCode` which is initially set by configuration, `200` by default.\n\n## `writeEmpty`\nInvoked by `write` for `undefined` incoming value. Writes an empty [204 No Content](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204) response.\n\n## `writeStream`\nThis method is directly invoked by `write` for a [Readable](https://nodejs.org/api/stream.html#readable-streams) incoming value. It basically just pipes the argument into `.response`, but, first, it writes HTTP headers, `ContentType` specifically.\n\nTo overwrite the default `application/octet-stream` type, there are three options:\n* add the `[HttpRequestContext.CONTENT_TYPE]` property to the stream instance;\n* set `ctx.contentType`;\n* lowest level: explicitly call `ctx.response.setHeader ('content-length', ...)`.\n\n## `writeBuffer`\nInvoked by `write` for a [Buffer](https://nodejs.org/api/buffer.html) incoming value, writes the binary content supplied into `.response`.\n\nThe `ContentType` is controlled the same 3 ways as for `writeStream`, including the `[HttpRequestContext.CONTENT_TYPE]` property for the buffer.\n\n## `writeText`\nInvoked by `write` for a `string` incoming value. Unlike aforementioned methods, defaults `ContentType` to `text/plain`. Moreover, appends the `; charset=${charset}` unless it's already there. The same `charset` is used to translate the string into a Buffer to actually write out. One should have a really good reason to overwrite the default `charset: 'utf-8'`.\n\n## `writeObject`\nInvoked by `write` for a plain (not [Readable](https://nodejs.org/api/stream.html#readable-streams) nor [Buffer](https://nodejs.org/api/buffer.html)) object. Executes `stringify` to obtain the string representation and then uses `writeText` to do the rest. The `ContentType` is set to 'application/json'.\n\nTo build an SOAP service, `serialize` should be implemented with [XMLSchemata](https://github.com/do-/node-xml-toolkit/wiki/XMLSchemata) or like; and `contentType` must be set accordingly.\n\n# Reporting Errors\nThe `writeError` method requires an `Error` object as an argument. Normally, it should be created with [`http-errors`](https://www.npmjs.com/package/http-errors). Otherwise, the local `createError` option is used to wrap it up first — it absolutely must return an `http-errors` augmented object. \n\nThe result is written out with `writeText`. For a true `expose` value the text is the error's `message`; otherwise, just the status text is written.\n\nAs for any text, the `Content-Type` is `text/plain` by default, but it may me altered by setting `{headers: {'Content-Type': ...}}`.\n\nFor `writeError`, the `statusCode` is copied from the error object, overriding what was set for the context instance.\n\n# Working with Cookies\n`HttpRequestContext` wraps around the ultra popular [`cookie`](https://www.npmjs.com/package/cookie) module featuring:\n* the `cookieParams` property for reading the `.request`'s [`Cookie`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie) header;\n* the `setCookie (name, value, options)` method for writing the `.response`'s [`Set-Cookie`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) header.\n\n# Going Low Level\nTo perform any tasks uncovered by the methods described, `HttpRequestContext` provides the next properties:\n\nName | Type | Description | Possible use\n-|-|-|-\n`request` | [`ClientRequest`](https://nodejs.org/api/http.html#class-httpclientrequest) | The raw request | Processing the body as a stream; see `keepBody` option\n`response` | [`ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) | The raw response | Setting headers\n`url` | [`URL`](https://nodejs.org/api/url.html) | Parsed `request.url` | Reading non standard parameters\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdo-%2Fnode-http-server-tools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdo-%2Fnode-http-server-tools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdo-%2Fnode-http-server-tools/lists"}