{"id":22356325,"url":"https://github.com/do-/node-doix-http","last_synced_at":"2026-01-04T08:03:38.731Z","repository":{"id":65148725,"uuid":"583931595","full_name":"do-/node-doix-http","owner":"do-","description":"doix HTTP binding","archived":false,"fork":false,"pushed_at":"2024-09-13T08:42:15.000Z","size":732,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-20T16:04:14.215Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","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":"2022-12-31T14:14:13.000Z","updated_at":"2024-09-13T08:42:19.000Z","dependencies_parsed_at":"2024-05-30T21:12:09.906Z","dependency_job_id":"bb38c000-05b9-47a5-b473-b4660240134d","html_url":"https://github.com/do-/node-doix-http","commit_stats":{"total_commits":80,"total_committers":2,"mean_commits":40.0,"dds":0.0625,"last_synced_commit":"8181e8e09fb85050497d8a2f90d70190f1f8fe09"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/do-%2Fnode-doix-http","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/do-%2Fnode-doix-http/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/do-%2Fnode-doix-http/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/do-%2Fnode-doix-http/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/do-","download_url":"https://codeload.github.com/do-/node-doix-http/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227511748,"owners_count":17782626,"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":[],"created_at":"2024-12-04T14:09:54.457Z","updated_at":"2026-01-04T08:03:33.698Z","avatar_url":"https://github.com/do-.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![workflow](https://github.com/do-/node-doix-http/actions/workflows/main.yml/badge.svg)\n![Jest coverage](./badges/coverage-jest%20coverage.svg)\n\n`doix-http` is an addon for [`doix`](https://github.com/do-/node-doix) framework for adding [Web services](https://www.w3.org/TR/ws-arch/) to `doix` [applications](https://github.com/do-/node-doix/wiki/Application).\n\n# Basic usage\nIn this section, a trivial sample echo service is used to illustrate some of the framework's basic concepts. \n\n## Prerequisites\n### Installation\nFirst, as usual:\n```\nnpm init\nnpm install doix-http\n```\n### Application Code\nNext, create a module that implements the necessary business logic. Make a directory, it's path will be referred as `root`. Put a file called `${root}/Echo.js` in there:\n```js\nmodule.exports = {\n  getList: function () {\n    return this.request\n  },\n}\n```\nHere, `this` is an instance of `doix` [`Job`](https://github.com/do-/node-doix/wiki/Job), so `this.request` is the collection of all incoming parameters (loosely based on [PHP's `$_REQUEST`](https://www.php.net/manual/en/reserved.variables.request.php)).\n\nThe method name `getList` here is determined by the application's [naming conventions](https://github.com/do-/node-doix/wiki/NamingConventions). Feel free to change it if necessary.\n\n### `index.js` Preamble\nIn the directory where `npm init` was executed, place the file named `index.js` which contains:\n```js\nconst root = '...', host = '127.0.0.1', port = 8000 // or read them from some config file\n\nconst http          = require ('http')\nconst winston       = require ('winston')\nconst {Application} = require ('doix')\nconst {WebService}  = require ('doix-http')\n\nconst logger = winston.createLogger ({transports: [new winston.transports.Console ()]})\nconst app    = new Application ({modules: {dir: {root}}, logger})\n// now, the service...\n```\n\nCreating a [winston](https://github.com/winstonjs/winston) logger is required here, as `doix` uses it to automatically [watch on everything](https://github.com/do-/node-events-to-winston).\n\n## Creating the Web Service\nNow, append the following lines to the `index.js`:\n### Standalone\n```js\n// now, the service...\nconst ws  = new WebService (app, {name: 'ws', methods: ['GET', 'POST']})\nconst srv = new http.Server ((_, res) =\u003e ws.process (res))\nsrv.listen ({host, port})\n```\nRun it and check `http://127.0.0.1:8000/?type=echo`. The response body must be `{\"type\":\"echo\"}`. The `type` value refers to the module name (so, the filename `'Echo.js'`). Without it, you'll get the [500 Internal Server Error](https://developer.mozilla.org/ru/docs/Web/HTTP/Status/500).\n\nAdd some more search parameters (but not `id`, `action` and `part`, as they [affect](https://github.com/do-/node-doix/wiki/NamingConventions) the choice of method), and watch them appear in the response. Try some `POST` requests with valid JSON as body: this content will be merged with the parameters read from the URL string.\n\n### Plugged into a Router\nIn the example above, the bare standard [http.Server](https://nodejs.org/docs/latest/api/http.html#class-httpserver) just feeds incoming [http.ServerResponse](https://nodejs.org/docs/latest/api/http.html#class-httpserverresponse) instances to the [`ws.process`](https://github.com/do-/node-doix-http/wiki/WebService#process-response) method. It basically works, but to further develop such code in a maintainable way, one absolutely need to add here at least some logging and error handling. Having multiple services with the same host:port, distinct by some conditions on the URL is, too, a must.\n\nThere are lots of solutions to the above problems. Here, we'll stick with one of them, the [`HttpRouter`](https://github.com/do-/node-protocol-agnostic-router/wiki/HttpRouter), from a companion module to be installed separately first:\n\n```sh\nnpm install protocol-agnostic-router\n```\n\nThat done, we can rewrite the ending of our script as\n\n```js\n// now, the service...\nconst {HttpRouter}  = require ('protocol-agnostic-router')\n\nconst router = new HttpRouter ({name: 'EndPoint', listen: {host, port}, logger})\nrouter.add (new WebService (app, {name: 'ws', location: '/api-v1', methods: ['GET', 'POST']}))\n// router.add (new WebService (app, {name: 'ws1', location: '/api-v2', methods: ['GET', 'POST']}))\n\nrouter.listen ()\n```\n\nThe same (easily replaceable) winston logger is used to track the life cycle of the router, including intercepted errors. Multiple virtual hosts can be added at once (not necessarily WebService instances, but anything capable of handling `response` objects).\n\n# Options Reference\nHaving shown the very basic case of `WebService` working somehow, we now present the complete example of its constructor call with all possible options (most of which belong to either [JobSource](https://github.com/do-/node-doix/wiki/JobSource) or [HttpRequestContext](https://github.com/do-/node-http-server-tools/wiki)):\n```js\nconst ws = new WebService (app, {\n\n        name: 'myEndPoint',\n\n//      location: '/my-end-point/',\n// OR\ttest: req =\u003e req.url.slice (0, 4) === '/roo',\n\n\tmethods: ['POST'],\n\t\n//      request     : {},\n//      getRequest  : http =\u003e {...http.pathParams, ...http.searchParams, ...http.bodyParams},\n//      parse       : str =\u003e JSON.parse (str),        // for .bodyParams\n//      keepBody    :       // when NOT to read it, e.g. function () {return this.path [0] === '~raw'},\n//      maxBodySize : 10 * 1024 * 1024,\n//      pathBase    : 0,\n//      pathMapping :       // `path` =\u003e `pathParams`, e.g. ([type, id]) =\u003e ({type, id})\n\n//      statusCode  : 200,\n//      contentType : 'application/json',\n//      charset     : 'utf-8',\n//      stringify   : obj =\u003e JSON.stringify (obj),    // for .write ({...})\n//      createError : err =\u003e createError (500, err, {expose: false}),\n\n//\ton:         {\n//        init:    [],\n//        start:   [],\n//        end:     [\n//          // e.g. async function () {this.result = this.result ?? {}}\n//        ],\n//        error:   [],\n//        finish:  [],\n//      },\n\n//      maxLatency: Infinity,\n//      maxPending: Infinity,\n\n//\tglobals:    {},\n//\tgenerators: {},\n//      pools:      {},\n//      logger:     app.logger,\n\n})\n```\n\nHere, \n* uncommented lines show mandatory parameters with sample values;\n* commented ones are for omittable options and:\n  * explicit values (like for `maxBodySize`) show hardcoded defaults; \n  * second comments with _e.g._ (see `pathMapping`) suggest some usable values where no default is set.\n\nIn the following, each option is described in detail.\n\n## Core\n### `name`\nThe unique name of this Web service among all [job sources](https://github.com/do-/node-doix/wiki/JobSource) in the containing [Application](https://github.com/do-/node-doix/wiki/Application).\n\nSimply put, the technical name to appear in logs.\n### `methods`\nThe mandatory non empty array of [HTTP request methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods), uppercase, to detect requests subject to be closed with [405 Method Not Allowed](https://developer.mozilla.org/ru/docs/Web/HTTP/Status/405) without any further processing.\n\n## Reading Request\nOptions from this section affect the way the object visible as `this.request` is constructed from the incoming HTTP message.\nIn addition, the application code can operate on the [HttpRequestContext](https://github.com/do-/node-http-server-tools) instance `this.http`, specifically:\n\n* `this.http.request`: the original [ClientRequest](https://nodejs.org/api/http.html#class-httpclientrequest);\n* `this.http.url`: the reconstructed [URL](https://nodejs.org/api/url.html);\n* `this.http.path`: the URL's pathname, split by '/' cleaned up, with `pathBase` elements omitted;\n* `this.http.body`: the whole body as a [Buffer](https://nodejs.org/docs/latest/api/buffer.html#buffer);\n* `this.http.bodyText`: the same, as a string.\n\n### `request`\nThe content of this object, which is empty by default, is copied into every `this.request`, with the lowest priority.\n\n### `getRequest`\nThis function with the [HttpRequestContext](https://github.com/do-/node-http-server-tools/wiki#reading-incoming-data) argument merges parameters read from three different HTTP request parts into one object.\n\nYou can use it, for example, to restrict the source of the input.\n```js\ngetRequest: http =\u003e http.bodyParams, // the URL will be ignored\n```\n\nInstead of setting this option, you can rewrite the method of the same name, which can be useful for adding new parameter sources:\n```js\nclass extends WebService {\n  getRequest (http) {\n    return {\n      ...super.getRequest (http),\n      _secret: http.request.header ['x-my-secret'],\n    }\n  }\n}\n```\n### `parse`\nThis function, which defaults to [`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) is used to convert the HTTP request body, which is read completely as a string, into the `.bodyParams' object. \n\nFor SOAP services, an XML parser must be used here.\n\nIn some cases, the developer may choose to set it to an empty function and operate directly on `this.http.bodyText` or `this.http.body`.\n\n### `keepBody`\nNormally, for `POST` / `PUT` / `PATCH` requests, `WebService` tries to read the whole body before continuing with `getRequest`, to make `bodyParams` available.\n\nIn some cases, however, the business method needs to read it progressively, as a [Readable stream](https://nodejs.org/docs/latest/api/stream.html#readable-streams). To make this possible, the `WebService` must be configured with `keepBody` returning `true` in such cases. Then, even for for `POST` / `PUT` / `PATCH` `this.http.bodyText` will be `''` and `this.http.bodyParams` — `{}`.\n\n### `maxBodySize`\nIf the request has the body and reading it is not prevented, its length, in bytes, must not exceed `maxBodySize`, which is 10 Mb by default. Otherwise, [413 Content Too Large](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413) will be thrown.\n\n### `pathBase`\nIf the WebService is co-located with several others and only receives requests whose URLs start with a fixed prefix, you may want to exclude it from the application's visibility. Consider a URL like `http://127.0.0.1/api/v.1.0.1/users/1`: the `api/v.1.0.1` part here is likely to be subject to any environment configuration while `users/1` carries the business data.\n\nBy setting `pathBase` to `2` in this case, we restrict `path` to `['users', '1']`. Also, the API version will not pollute the `pathParams` (if any, see right below).\n\n### `pathMapping`\nUnlike many well-known RESTful frameworks, `doix-http` doesn't map URLs to individual methods using global [NamingConventions](https://github.com/do-/node-doix/wiki/NamingConventions) instead. However, it's possible to use a common REST-like pattern, e.g. by, reading the `type` parameter from the root part of the [URL pathname](https://developer.mozilla.org/en-US/docs/Web/API/URL/pathname) and 'id' from the next one (if any). To do this, you should configure \n```js\npathMapping: ([type, id]) =\u003e ({type, id})\n```\nas shown above. This will be used to construct `this.http.pathParams`. By default, `pathParams` is always here and even merged into `this.request`, but, without `pathMapping` defined, it's an empty object `{}`.\n\n## Writing Response\nNormally, the business method called by `WebService` should return a plain Object. It's subject to `stringify` and output with `statusCode`, `contentType` and `charset`.\n\nIf a string is returned, `stringify` is skipped, but `contentType` and `charset` are applied.\n\nFor special objects representing binary content: [Buffers](https://nodejs.org/docs/latest/api/buffer.html#buffer) and [Readable streams](https://nodejs.org/docs/latest/api/stream.html#readable-streams), moreover, global `contentType` and `charset` are ignored, and streams are [piped](https://nodejs.org/docs/latest/api/stream.html#readablepipedestination-options) directly into the `this.http.response`.\n\nFinally, an empty [204 No Content](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204) response is generated for an `undefined` result.\n\nReturning any other type of result (functions, symbols etc.) will result in an error.\n\nSpeaking of errors: they can be explicitly thrown from within a business method instead of returning any result. Or they can be generated by different parts of the framework or external libraries. Either way, for each special [http error](https://www.npmjs.com/package/http-errors), its meta information is used to construct the HTTP response: the `statusCode` overrides any previously set; in case of falsy `expose` the `message` is replaced by the fixed status text. Any generic `Error` is wrapped up with the configured `createError`. Finally, if the method throws a value of a random type, it's first converted into `Job.NotAnError` and then handled as described above.\n\nNote that, to generate the HTTP response, application developers are free to use (at their own risk, of course) [`this.http.write*`](https://github.com/do-/node-http-server-tools/wiki#writing-results) methods or, eventually, the raw [`this.http.response`](https://nodejs.org/docs/latest/api/http.html#class-httpserverresponse).\n\nFurther down in this section, the options that affect the response are described in detail.\n### Normal Results (Not Errors)\n#### `statusCode`\nThe default [response.statusCode](https://nodejs.org/docs/latest/api/http.html#responsestatuscode). To set a non standard value in a specific business method, `this.http.statusCode` is available:\n```js\nthis.http.statusCode = 201\n// TIMTOWTDI\n// this.http.response.statusCode = 201\n```\nIn case of error, it is overridden anyway.\n#### Serializable Results\nThe options listed here apply to results that are neither [Buffers](https://nodejs.org/docs/latest/api/buffer.html#buffer) nor [Readable streams](https://nodejs.org/docs/latest/api/stream.html#readable-streams)\n\n##### `stringify`\nThis function, which defaults to [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify), this function is used to convert the value returned by the method (or, more precisely, `this.result` set by `doix`) into the string to be written as the HTTP response body.\n\n##### `contentType`\nThe [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) header value, unless explicitly set:\n\n```js\nthis.http.contentType = 'text/cryptic'\n// or even\n// this.http.response.setHeader ('content-type', 'application/warm')\n```\n\n##### `charset`\nThe `charset` option appended to the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) header (including the default  `application/octet-stream`) and used to encode the `stringify` result. Better left `'utf-8'`.\n\n### Errors\n#### `createError`\nThis function must translate a generic [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object into an [http error](https://www.npmjs.com/package/http-errors). By default, wraps it into [500 Internal Server Error](https://developer.mozilla.org/ru/docs/Web/HTTP/Status/500). \n\nIn certain applications, it may detect specific meta-information and set special status codes such as [422 Unprocessable Content](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422) for validation errors.\n\n## Workflow Extension Points\n### `on` \nThis [JobSource](https://github.com/do-/node-doix/wiki/JobSource) option is a bag of lists of additional handlers that the WebService assigns for  [events](https://github.com/do-/node-doix/wiki/Job#events) occurring during the [Job](https://github.com/do-/node-doix/wiki/Job) lifecycle. Specifically:\n\n| Event | New properties available | Possible use | \n| - | - | - |\n| `'init'` | `this.http` | Custom parsing / validation |\n| `'start'` | `this.method`, `this.user` | Security checks |\n| `'end'` | `this.result` | Result rewriting |\n| `'error'` | `this.error` | Error rewriting |\n\nIn particular, the `'end'` handler can modify `this.result` by adding some meta information: in this case, the corrected value will be subject to `stringify` instead of the original business method result.\n\nUnlike the standard [Events](https://nodejs.org/docs/latest/api/events.html#events), handlers can be asynchronous here. For example, the [doix-http-cookie-redis](https://github.com/do-/node-doix-http-cookie-redis) addon asynchronously fetches session data from an external resource at `'init'` and stores it back at `'end'`.\n\nMultiple asynchronous handlers for an event are executed with [Promise.all](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all), they cannot depend on each other. For example, adding a second `'end'` handler that rewrites `this.result` will make the result unpredictable.\n\nTo ensure the order of execution for `'init'`, `'end'` and `'error'` events, you can rewrite the corresponding [methods](https://github.com/do-/node-doix-http/wiki/WebService#onjobinit-job) instead of configuring handlers.\n\n## Routing\nOptions in this section work only with [`HttpRouter`](https://github.com/do-/node-protocol-agnostic-router/wiki/HttpRouter).\n### `location` \nLike [Apache httpd's `Location`](https://httpd.apache.org/docs/2.4/mod/core.html#location), \n[nginx' `location`](https://nginx.org/en/docs/http/ngx_http_core_module.html#location) and similar directives. \n\nThis can be a string or a regular expression. For a `location` given as a string, the `pathBase` is set automatically. Otherwise, the developer should set it explicitly.\n\n### `test` \nThis function, if set, receives each [request](https://nodejs.org/docs/latest/api/http.html#class-httpclientrequest) incoming to the router and must return a Boolean value indicating whether the WebService accepts the message for processing. This is a more flexible alternative to `location`.\n\n## Bandwidth Control\n### `maxLatency`\nThis option can (and should) be set to a finite positive number indicating the maximum time to wait for the business method to return a response, in milliseconds. If the time expires without a result being returned, an error is thrown.\n\nFor web UI backend services in particular, it's a good idea to set `maxLatency: 10000`. Staring at a web form that is stuck for 10 seconds will make any user think the server is dead anyway.\n\n### `maxPending`\nThis is the maximum number of requests to handle simultaneously. With `maxPending` requests in process, an attempt to take another will result in a `JobSource.OverflowError'.\n\nAt any time:\n* `ws.pending` is the [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) of all [Jobs](https://github.com/do-/node-doix/wiki/Job) in progress;\n* `ws.capacity` is the number of requests that can be accepted immediately (`Infinity` if `maxPending` is not set).\n\n## Context Injection\nEvery option in this section has a counterpart in [Application](https://github.com/do-/node-doix/wiki/Application). The application's defaults are inherited, only specific variables need to be set for each service.\n\n### `globals`\nEntries of this object will be copied into `this` [Job](https://github.com/do-/node-doix/wiki/Job) instance upon its creation. For example,\n```js\n//\nglobals: {xsd: mySchema},\n//\ngetList: async function () {\n  return this.xsd.serialize ([])\n}\n```\nmakes `this.wsdl` available when executing each business method. \n\n### `generators`\nSimilar to `globals`, this option contains no-arguments functions called to generate values for eponymous properties of `this`. Typically, they are used for one-off random values:\n```js\n//...\ngenerators: {noCache: () =\u003e Math.random ()},\n//...\ngetList: async function () {\n  return [this.noCache]\n}\n```\n\n### `pools`\nHere, [ResourcePool](https://github.com/do-/node-doix/wiki/ResourcePool)s are provided, first of all, [database connections](https://github.com/do-/node-doix-db/wiki/DbPool).\n```js\n//...\npools: {db: myDbPool},\n//...\ngetList: async function () {\n  return db.getArray ('SELECT * FROM my_table')\n}\n```\n## Miscellaneous\n### `logger`\nThe [winston](https://github.com/winstonjs/winston) logger used to automatically [track](https://github.com/do-/node-events-to-winston) the WebService lifecycle. Defaults to one used by the hosting [Application](https://github.com/do-/node-doix/wiki/Application).\n\n# See also\nThis library provides [`CookieSession`](https://github.com/do-/node-doix-http/wiki/CookieSession): a companion class to `WebService`, the base for implementing [cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) related session workflows, such as [`doix-http-cookie-redis`](https://github.com/do-/node-doix-http-cookie-redis) and [`doix-http-cookie-jwt`](https://github.com/do-/node-doix-http-cookie-jwt).\n\nMost of the `WebService`'s functionality is implemented via the [`HttpRequestContext`](https://github.com/do-/node-http-server-tools/wiki) class, which can be used alone.\n\nVery few Web services can be developed without interaction with database servers. The [`doix-db`](https://github.com/do-/node-doix-db/wiki) module offers an API that can be quite helpful when dealing with relational databases.\n\nTo delve deeper into the topic of `doix` applications, modules, jobs, resources etc., consider using the [core framework](https://github.com/do-/node-doix/wiki) documentation.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdo-%2Fnode-doix-http","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdo-%2Fnode-doix-http","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdo-%2Fnode-doix-http/lists"}