{"id":16186182,"url":"https://github.com/zenflow/composite-http-server","last_synced_at":"2026-01-20T04:32:35.301Z","repository":{"id":42812103,"uuid":"269246835","full_name":"zenflow/composite-http-server","owner":"zenflow","description":"Helps you to compose a single http server program from multiple constituent server programs (http or not).","archived":false,"fork":false,"pushed_at":"2023-01-06T07:51:10.000Z","size":618,"stargazers_count":1,"open_issues_count":17,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-21T08:59:07.591Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","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/zenflow.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}},"created_at":"2020-06-04T02:58:37.000Z","updated_at":"2023-03-04T06:20:25.000Z","dependencies_parsed_at":"2023-02-05T16:01:24.391Z","dependency_job_id":null,"html_url":"https://github.com/zenflow/composite-http-server","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/zenflow/composite-http-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenflow%2Fcomposite-http-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenflow%2Fcomposite-http-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenflow%2Fcomposite-http-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenflow%2Fcomposite-http-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zenflow","download_url":"https://codeload.github.com/zenflow/composite-http-server/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenflow%2Fcomposite-http-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28596079,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T02:08:49.799Z","status":"ssl_error","status_checked_at":"2026-01-20T02:08:44.148Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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-10-10T07:17:25.587Z","updated_at":"2026-01-20T04:32:35.284Z","avatar_url":"https://github.com/zenflow.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# composite-http-server\n\nHelps you to compose a single http server program from multiple constituent server programs (http or not).\n\nGiven a declarative configuration, including a description of a collection of constituent `servers`, it will:\n- Start and manage processes for constituent servers, interleaving and printing the stdout/stderr of each\n- Start an http server that proxies requests to the appropriate constituent server, as determined by configuration and the url path of the request\n\nDefine your composite server in a script like this one...\n\n```js\n// composite.js\n\nconst { startCompositeServer } = require('composite-http-server')\n\nstartCompositeServer({\n  defaultPort: 3000,\n  servers: [\n    {\n      label: 'my-service',\n      command: 'node my-service/server.js',\n      port: 3001,\n      httpProxyPaths: ['/api/my-service'],\n    },\n    {\n      label: 'web',\n      command: 'node web/server.js',\n      port: 3002,\n      httpProxyPaths: ['/'],\n      httpProxyOptions: {}\n    },\n  ],\n})\n```\n\nRun it...\n\n```\n$ node composite.js\nStarting server 'my-service'...\nStarting server 'web'...\nmy-service | [out] Started 🚀\nweb        | [out] Started 🚀\nStarted server 'web' @ http://localhost:3002\nStarted server 'my-service' @ http://localhost:3001\nStarting server '$proxy'...\nStarted server '$proxy' @ http://localhost:3000\nReady\n```\n\nNow you have an http server running at http://localhost:3000 which proxies requests to either of two underlying \"constituent\" servers:\n- all requests with URL under `/api/my-service` go to \"my-service\"\n- all other requests with URL under `/` go to \"web\"\n\n### Features\n\n- You can define a constituent server with **no** `httpProxyPaths`, if it is meant to be used only by other constituent servers and not accessible via the http proxy.\n- You can define a constituent server that is **not http**, as long as it conforms to the \"Specs for generic 'server program'\" as described below.\n- The composite server starts gracefully; It doesn't accept requests until every constituent server is accepting requests.\n- If any constituent server exits (i.e. crashes) the remaining constituent servers will be killed and the composite server will exit.\n\n### Specs for generic 'server program'\n\nThis describes behavior which is expected of the constituent servers you define, and which you can expect of the composite server.\n\n- should not exit by itself; should run until killed by another process\n- must serve over the port designated by the `PORT` environment variable\n\n### Configuration\n\n- `config.printConfig` (type: `boolean`; optional; default: `false`) If set to `true`, the effective configuration will be printed before starting the composite server. Useful for debugging dynamic configurations.\n- `config.defaultPort` (type: `number`; optional; default: `3000`) *Default* port on which to start the composite server. This is *only* used if the `PORT` environment variable is not defined.\n- `config.httpProxyOptions` (type: `HttpProxyOptions`; optional; default: `{}`) [http-proxy-middleware options](https://github.com/chimurai/http-proxy-middleware#options) (without `target` or `router`) to be used when proxying to *any* of the described `servers`. You can also set these options per-server with `config.servers[].httpProxyOptions`.\n- `config.servers[]` (required) Description of constituent servers. Must contain one or more elements.\n- `config.servers[].label` (type: `string`; optional; default: server's index in the array) Symbol used to identify this server.\n- `config.servers[].env` (type: `object`; optional; default: `{}`) Environment variables to define for this server's process. It will already inherit all the environment variables of it's parent (the composite server process), so there's no need to explicitly propagate environment variables in your configuration. The constituent server process will also already have `PORT` defined appropriately.\n- `config.servers[].command` (type: `string | string[]`; required) Command used to run the server. If it's a single string, it will be parsed into binary and argument strings. If it's an array of strings, the first element is the binary, and the remaining elements are the arguments. The server should behave according to \"Specs for generic 'http server program'\" (above).\n- `config.servers[].host` (type: `string`; optional; default: `'localhost'`) Hostname that this server can be expected to start on.\n- `config.servers[].port` (type: `number`; required) Port number that this server can be expected to start on. This is passed to the constituent server process as the `PORT` environment variable.\n- `config.servers[].httpProxyPaths` (type: `string[]`; optional; default: `[]`) Absolute paths to check when determining which server to proxy an http request to. Each request is proxied to the first server that has a path that the request path is within.\n- `config.servers[].httpProxyOptions` (type: `HttpProxyOptions`; optional; default: `{}`) [http-proxy-middleware options](https://github.com/chimurai/http-proxy-middleware#options) (without `target` or `router`) to be used for the http proxy to this server. You can also set these options globally with `config.httpProxyOptions`.\n\n## Motivation\n\nSometimes we may want to use some open-source (or just reusable) http server as a *component* of our app or service.\nIf we are thinking of that server as a *component* of our overall server, then we might want to include it *in* our\noverall server, rather than deploying it as its own independent service.\n\nAdvantages of the \"single server\" (or \"monolith\") approach:\n1. simplifies deployments \u0026 devops\n2. allows us to deploy to basically any PaaS provider\n3. allows us to effectively use PaaS features like [Heroku's Review Apps](https://devcenter.heroku.com/articles/github-integration-review-apps)\n4. with some PaaS providers (e.g. Heroku, render) saves the cost of hosting additional \"apps\" or \"services\"\n\nThere some real advantages of the \"multiple servers\" (or \"microservices\") approach too, which you should research for\nyourself. I think you will find that these benefits generally apply more to large-scale projects with many services,\nand maybe multiple teams. For smaller projects, it seems that the \"single service\" approach provides more advantage.\nBear in mind that serious projects can often benefit from *starting out* small, and splitting out into separate\nservices only as needed.\n\nIf you are unable build everything into a single http server program running as a single process type (by calling the\nserver code from your own code) for whatever reason (maybe the constituent server is written in a different language\nthan the rest of the project, maybe its source code is not available, maybe something else) then you can use\ncomposite-http-server to build everything into a single http server program running multiple process types internally.\n\n## Roadmap\n\n- use `npm-run-path` package\n- verify configured ports are available in \"pre-flight check\", \u0026 exit early/immediately if they are not\n- export a port finder utility (to be used with `start`)\n    - then it is possible to run tests concurrently (should we?)\n    - use it internally to get default `servers[].port`\n- perf optimization for tests on windows: when cleaning up \u0026 killing `proc`, don't wait whole time for proc to have `exited`\n- more tests for various configurations\n    - printConfig\n    - omitting server[].labels, ...\n    - config that fails validation (also, in source, handle: same port used twice, same label used twice, etc.)\n    - server with no `httpProxyPaths`\n    - glob patterns in `httpProxyPaths`\n    - httpProxyOptions \u0026 `server[].httpProxyOptions`\n\n## Feature ideas\n\n- `config.server[].handleExit` 'exit', 'restart', or function. Default 'exit' (which is only current behavior)\n- `config.server[].startupTimeout` milliseconds to wait for port to open before timeout error (currently it waits basically forever)\n- option to log requests in `$proxy` server\n- for *nix: graceful shutdown \u0026 `config.server[].forceKillTimeout` option (milliseconds to wait before sending SIGKILL)\n- `config.server[].scale`\n    - maybe: number of workers in node cluster (support node servers only)\n    - maybe: number of processes to start (requires configuring more port numbers \u0026 doing round-robin in proxy)\n- `config.server[].dependencies`: array of labels of servers to wait for to be ready before starting this server\n- use same node binary that main process was started with\n\n## Changelog\n\n- `v2.0.0`\n    - Run server procs w/o shell \u0026 kill server procs normally (w/o tree-kill) (32723c73467522551bc57da8575f57f59d04d11d)\n    - Ensure importing module is free of side-effects (efeab195b234cac153b601dd1e0835cbd53bcf2d)\n- `v1.1.0`\n    - Shutdown gracefully in non-windows environments (bce5500c99c6eec2acd7262ae70a4e6cb52b9d1c)\n- `v1.0.0`\n    - Initial commit\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzenflow%2Fcomposite-http-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzenflow%2Fcomposite-http-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzenflow%2Fcomposite-http-server/lists"}