{"_id":"@abinashpatri/orchestrator","_rev":"2-d8449ab1dce511fcefc6438aa1895bf7","name":"@abinashpatri/orchestrator","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@abinashpatri/orchestrator","version":"1.0.0","keywords":["consul","service-discovery","api-gateway","orchestrator","typescript"],"author":{"name":"Abinash Patri"},"license":"MIT","_id":"@abinashpatri/orchestrator@1.0.0","maintainers":[{"name":"abinashpatri","email":"abinashpatri33@gmail.com"}],"dist":{"shasum":"036faf4eeeeca494fcae2c5d0654c849b9355873","tarball":"https://registry.npmjs.org/@abinashpatri/orchestrator/-/orchestrator-1.0.0.tgz","fileCount":19,"integrity":"sha512-azJtBRXT3faYgoiE/VzXRO2jDv0e5U+8pIPMOD6FOQy5Fhd4KI8XKQqKXcFHZ3MLem+5OHAipnRWk7O9CAW1sw==","signatures":[{"sig":"MEYCIQDaHB/ZcnhlK/GgnzABKWeW3BMal+QOFX+77QhyVOMsPwIhANZ1yPw+y65diO+Gqif/YWGwtqFLCDm3KY4JXzUkScYB","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":37284},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"},"./express":{"types":"./dist/express.d.ts","import":"./dist/express.mjs","require":"./dist/express.js"}},"scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run build"},"_npmUser":{"name":"abinashpatri","email":"abinashpatri33@gmail.com"},"_npmVersion":"11.6.2","description":"Reusable TypeScript/JavaScript service discovery and API gateway toolkit","directories":{},"_nodeVersion":"24.11.1","dependencies":{"consul":"^2.0.1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","eslint":"^10.1.0","express":"^5.1.0","prettier":"^3.8.1","typescript":"^5.9.3","@types/node":"^25.5.0","@types/express":"^5.0.6","http-proxy-middleware":"^3.0.5"},"peerDependencies":{"express":">=4.18.0","http-proxy-middleware":">=3.0.0"},"peerDependenciesMeta":{"express":{"optional":true},"http-proxy-middleware":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/orchestrator_1.0.0_1774244181924_0.12522845770563307","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@abinashpatri/orchestrator","version":"1.0.1","description":"Reusable TypeScript/JavaScript service discovery and API gateway toolkit","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","require":"./dist/index.js","import":"./dist/index.mjs"},"./express":{"types":"./dist/express.d.ts","require":"./dist/express.js","import":"./dist/express.mjs"}},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run build"},"keywords":["consul","service-discovery","api-gateway","orchestrator","typescript"],"author":{"name":"Abinash Patri"},"license":"MIT","type":"commonjs","devDependencies":{"@types/express":"^5.0.6","@types/node":"^25.5.0","eslint":"^10.1.0","express":"^5.1.0","http-proxy-middleware":"^3.0.5","prettier":"^3.8.1","tsup":"^8.5.1","typescript":"^5.9.3"},"peerDependencies":{"express":">=4.18.0","http-proxy-middleware":">=3.0.0"},"peerDependenciesMeta":{"express":{"optional":true},"http-proxy-middleware":{"optional":true}},"dependencies":{"consul":"^2.0.1"},"_id":"@abinashpatri/orchestrator@1.0.1","_nodeVersion":"24.11.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-sdc2nmuSpz2nxrE1TwvsLLduCfnD49owHW4+LquIfT5Pnr5pWXrwllFK+RxuzEGSxINEWo7sepIbR0Jf5enXKA==","shasum":"963e3a837d3a7d77a7645a06c26865b2779b0bb5","tarball":"https://registry.npmjs.org/@abinashpatri/orchestrator/-/orchestrator-1.0.1.tgz","fileCount":19,"unpackedSize":38477,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDWIxhjeZE8RyF2ILJQ/3r/5hLAXsIQkSR92aJc6AsZ3AiADDVELpypgyYd6PoPapWj2zwdnBxorWwkkkqQh5nJClw=="}]},"_npmUser":{"name":"abinashpatri","email":"abinashpatri33@gmail.com"},"directories":{},"maintainers":[{"name":"abinashpatri","email":"abinashpatri33@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/orchestrator_1.0.1_1774246636725_0.04149293837228085"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-23T05:36:21.775Z","modified":"2026-03-23T06:17:16.990Z","1.0.0":"2026-03-23T05:36:22.070Z","1.0.1":"2026-03-23T06:17:16.870Z"},"author":{"name":"Abinash Patri"},"license":"MIT","keywords":["consul","service-discovery","api-gateway","orchestrator","typescript"],"description":"Reusable TypeScript/JavaScript service discovery and API gateway toolkit","maintainers":[{"name":"abinashpatri","email":"abinashpatri33@gmail.com"}],"readme":"# @abinashpatri/orchestrator\n\nReusable TypeScript/JavaScript toolkit for service registration, service\ndiscovery, and API gateway routing.\n\nWorks in both ESM and CommonJS projects with full TypeScript type support.\n\nCore APIs (`createServiceClient`, `registerService`, `deregisterService`, `getServiceUrl`) are framework-agnostic.\nExpress gateway APIs are available from the subpath `@abinashpatri/orchestrator/express`.\n\n## Install\n\n```bash\nnpm install @abinashpatri/orchestrator\n```\n\nFor Express gateway adapter, install peer dependencies in your microservice:\n\n```bash\nnpm install express http-proxy-middleware\n```\n\n## Prerequisites\n\n- Consul agent is running and reachable\n- your services expose a health endpoint (default: `/health`)\n- Node.js 18+ recommended\n\n---\n\n## Step-by-step usage (modern way)\n\n## 1) Create a Consul client\n\n```ts\nimport { createServiceClient } from \"@abinashpatri/orchestrator\";\n\nconst consul = createServiceClient({\n  host: process.env.CONSUL_HOST ?? \"127.0.0.1\",\n  port: Number(process.env.CONSUL_PORT ?? 8500),\n  token: process.env.CONSUL_HTTP_TOKEN, // optional\n  secure: process.env.CONSUL_SECURE === \"true\", // optional\n});\n```\n\n## 2) Register your service\n\n```ts\nimport { registerService } from \"@abinashpatri/orchestrator\";\n\nconst serviceId = await registerService(consul, {\n  serviceName: \"user-service\",\n  serviceId: `user-service-${process.pid}`, // optional (auto-generated if not provided)\n  address: \"127.0.0.1\",\n  port: 3001,\n  tags: [\"v1\", \"public\"], // optional\n  healthPath: \"/health\", // optional, default: /health\n  healthInterval: \"10s\", // optional, default: 10s\n  healthTimeout: \"5s\", // optional, default: 5s\n  deregisterCriticalServiceAfter: \"30s\", // optional, default: 30s\n});\n```\n\n## 3) Discover another service URL\n\n```ts\nimport { getServiceUrl } from \"@abinashpatri/orchestrator\";\n\nconst paymentServiceUrl = await getServiceUrl(consul, \"payment-service\");\n// Example output: http://127.0.0.1:4002\n```\n\nBy default, discovery returns a random healthy instance (simple load balancing).\n\n## 4) Build an API gateway app\n\n```ts\nimport { createGatewayApp } from \"@abinashpatri/orchestrator/express\";\nconst userHeaderMiddleware = (req, res, next) => {\n  req.headers[\"x-service-source\"] = \"api-gateway\";\n  next();\n};\n\nconst paymentAuthMiddleware = (req, res, next) => {\n  if (!req.headers.authorization) {\n    res.status(401).json({ error: \"Missing Authorization header\" });\n    return;\n  }\n  next();\n};\n\nconst app = createGatewayApp(consul, {\n  healthPath: \"/health\",\n  routes: [\n    {\n      serviceName: \"user-service\",\n      routePrefix: \"/api/users\",\n      middlewares: [userHeaderMiddleware],\n    },\n    {\n      serviceName: \"payment-service\",\n      routePrefix: \"/api/payments\",\n      middlewares: [paymentAuthMiddleware],\n    },\n    { serviceName: \"notification-service\" }, // defaults to /api/notification-service\n  ],\n});\n\nconst server = app.listen(4000, () => {\n  console.log(\"Gateway running on :4000\");\n});\n```\n\n## 5) Graceful shutdown (important for production)\n\n```ts\nimport { deregisterService } from \"@abinashpatri/orchestrator\";\n\nfunction shutdown(signal: string) {\n  return async () => {\n    console.log(`Received ${signal}, shutting down...`);\n\n    server.close(async () => {\n      try {\n        await deregisterService(consul, serviceId);\n      } finally {\n        process.exit(0);\n      }\n    });\n  };\n}\n\nprocess.on(\"SIGINT\", shutdown(\"SIGINT\"));\nprocess.on(\"SIGTERM\", shutdown(\"SIGTERM\"));\n```\n\n---\n\n## Full TypeScript example\n\n```ts\nimport {\n  createServiceClient,\n  registerService,\n  deregisterService,\n  getServiceUrl,\n} from \"@abinashpatri/orchestrator\";\nimport { createGatewayApp } from \"@abinashpatri/orchestrator/express\";\n\nconst consul = createServiceClient({\n  host: process.env.CONSUL_HOST ?? \"127.0.0.1\",\n  port: Number(process.env.CONSUL_PORT ?? 8500),\n  token: process.env.CONSUL_HTTP_TOKEN,\n});\n\nconst serviceId = await registerService(consul, {\n  serviceName: \"api-gateway-service\",\n  address: \"127.0.0.1\",\n  port: 4000,\n  healthPath: \"/health\",\n});\n\nconst app = createGatewayApp(consul, {\n  routes: [\n    { serviceName: \"user-service\", routePrefix: \"/api/users\" },\n    { serviceName: \"notification-service\", routePrefix: \"/api/notifications\" },\n  ],\n});\n\napp.get(\"/where-is-user-service\", async (_req, res) => {\n  try {\n    const url = await getServiceUrl(consul, \"user-service\");\n    res.json({ service: \"user-service\", url });\n  } catch (error) {\n    const message = error instanceof Error ? error.message : \"Unknown error\";\n    res.status(503).json({ error: message });\n  }\n});\n\nconst server = app.listen(4000, () => {\n  console.log(\"API gateway listening on port 4000\");\n});\n\nasync function cleanup() {\n  await deregisterService(consul, serviceId);\n}\n\nprocess.on(\"SIGINT\", async () => {\n  server.close(async () => {\n    await cleanup();\n    process.exit(0);\n  });\n});\n\nprocess.on(\"SIGTERM\", async () => {\n  server.close(async () => {\n    await cleanup();\n    process.exit(0);\n  });\n});\n```\n\n---\n\n## JavaScript (CommonJS) usage\n\n```js\nconst {\n  createServiceClient,\n  registerService,\n  getServiceUrl,\n  deregisterService,\n} = require(\"@abinashpatri/orchestrator\");\nconst { createGatewayApp } = require(\"@abinashpatri/orchestrator/express\");\n\nasync function main() {\n  const consul = createServiceClient({\n    host: \"127.0.0.1\",\n    port: 8500,\n    token: process.env.CONSUL_HTTP_TOKEN,\n  });\n\n  const serviceId = await registerService(consul, {\n    serviceName: \"api-gateway-service\",\n    address: \"127.0.0.1\",\n    port: 4000,\n  });\n\n  const app = createGatewayApp(consul, {\n    routes: [{ serviceName: \"user-service\", routePrefix: \"/api/users\" }],\n  });\n\n  app.get(\"/discover-user-service\", async (_req, res) => {\n    const url = await getServiceUrl(consul, \"user-service\");\n    res.json({ url });\n  });\n\n  const server = app.listen(4000);\n\n  process.on(\"SIGINT\", async () => {\n    server.close(async () => {\n      await deregisterService(consul, serviceId);\n      process.exit(0);\n    });\n  });\n}\n\nmain().catch((err) => {\n  console.error(err);\n  process.exit(1);\n});\n```\n\n---\n\n## API reference\n\n### `createServiceClient(options?)`\n\nCreates and returns a Consul client.\n\n`options`:\n\n- `host?: string` (default: `127.0.0.1`)\n- `port?: number` (default: `8500`)\n- `token?: string`\n- `secure?: boolean` (default: `false`)\n\n### `registerService(consulClient, options)`\n\nRegisters a service and returns the final `serviceId`.\n\n`options`:\n\n- `serviceName: string` (required)\n- `serviceId?: string`\n- `address: string` (required)\n- `port: number` (required)\n- `tags?: string[]`\n- `healthPath?: string` (default: `/health`)\n- `healthInterval?: string` (default: `10s`)\n- `healthTimeout?: string` (default: `5s`)\n- `deregisterCriticalServiceAfter?: string` (default: `30s`)\n\n### `deregisterService(consulClient, serviceId)`\n\nRemoves a registered service from Consul.\n\n### `getServiceUrl(consulClient, serviceName, options?)`\n\nReturns one discovered service URL, such as `http://127.0.0.1:3001`.\n\n`options`:\n\n- `passing?: boolean` (default: `true`)\n\n### `createServiceProxy(consulClient, options)` (from `@abinashpatri/orchestrator/express`)\n\nCreates an Express middleware that resolves service target dynamically via Consul.\n\n`options`:\n\n- `serviceName: string` (required)\n- `routePrefix?: string` (default: `/api/${serviceName}`)\n\n### `createGatewayApp(consulClient, options)` (from `@abinashpatri/orchestrator/express`)\n\nCreates an Express app and mounts service proxies for all routes.\n\n`options`:\n\n- `routes: Array<{ serviceName: string; routePrefix?: string; middlewares?: RequestHandler[] }>` (required)\n- `healthPath?: string` (default: `/health`)\n\n---\n\n## Notes\n\n- If Consul returns `host.docker.internal`, the library auto-resolves it to `127.0.0.1` for host-local calls.\n- Keep service names consistent across registration and discovery.\n- For production, always use graceful shutdown to avoid stale Consul registrations.\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}