{"_id":"@danqiusheng/nest-nacos","_rev":"3-bd955a6eb85372e114aec763e94c904f","name":"@danqiusheng/nest-nacos","dist-tags":{"latest":"1.2.0"},"versions":{"1.0.0":{"name":"@danqiusheng/nest-nacos","version":"1.0.0","keywords":["nestjs","nest","nacos","service-discovery","config-center","configuration","microservice","grpc"],"author":{"name":"丹丘生"},"license":"MIT","_id":"@danqiusheng/nest-nacos@1.0.0","maintainers":[{"name":"danqiusheng","email":"LZCNice2021@163.com"}],"homepage":"https://github.com/danqiusheng/nest-nacos#readme","bugs":{"url":"https://github.com/danqiusheng/nest-nacos/issues"},"dist":{"shasum":"a0d9db4707ffec99219894268ae69a3f1683020c","tarball":"https://registry.npmjs.org/@danqiusheng/nest-nacos/-/nest-nacos-1.0.0.tgz","fileCount":39,"integrity":"sha512-nogC5NAOAe18dBOy9+i6CoTCKDf0MoI9hJSN3ebWzRfSx5MRayLu8Eez9av5f9qKaTv1d/Xg6HJ+2MH5zmnLjw==","signatures":[{"sig":"MEUCIE40O12Da43AwxDi6Gw5FOYeEdiTrPBms7LxcGw6sI3rAiEAy6Ga9VCx3SRETZQBZyPHtElulZnROUxp9o24xf3RtFc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":77054},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=16"},"scripts":{"build":"rimraf dist && tsc","version":"npm run build","postversion":"git push && git push --tags","start:example":"ts-node example/app.module.ts","prepublishOnly":"npm run build"},"_npmUser":{"name":"danqiusheng","email":"LZCNice2021@163.com"},"repository":{"url":"git+https://github.com/danqiusheng/nest-nacos.git","type":"git"},"_npmVersion":"10.9.4","description":"NestJS Nacos 插件 - 服务发现 + 配置中心（基于 nacos-sdk-nodejs，支持 gRPC / Nacos 2.x & 3.x）","directories":{},"_nodeVersion":"22.16.0","_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.2","nacos":"^2.6.3","rimraf":"^5.0.0","ts-node":"^10.9.0","typescript":"^5.4.0","@types/node":"^26.1.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","@nestjs/config":"^4.0.0","@nestjs/testing":"^11.1.28","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","nacos":"^2.6.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-nacos_1.0.0_1785467700222_0.8742336477706922","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@danqiusheng/nest-nacos","version":"1.1.0","keywords":["nestjs","nest","nacos","service-discovery","config-center","configuration","microservice","load-balancing"],"author":{"name":"danqiusheng"},"license":"MIT","_id":"@danqiusheng/nest-nacos@1.1.0","maintainers":[{"name":"danqiusheng","email":"LZCNice2021@163.com"}],"homepage":"https://gitee.com/li-zucheng/nacos-plugin","bugs":{"url":"https://gitee.com/li-zucheng/nacos-plugin/issues"},"dist":{"shasum":"dae6cb728f95bcbaeaf01cf25a8b40ef368a051c","tarball":"https://registry.npmjs.org/@danqiusheng/nest-nacos/-/nest-nacos-1.1.0.tgz","fileCount":45,"integrity":"sha512-fhT1bSBjyu2AlIV0eP5AtnT712oA/D8DSkwxLg7V+pyq4g+MyrGeDr4E5Mf4s6LL9V3iLv4W4w3xxgTTy0oDvA==","signatures":[{"sig":"MEUCIQDk/xtd0gYBQPX9GpjNwO4X67BMxJmX9ax2Xa5pG4HhDwIgODmbThSQccCrfvMOewDZCDhwmeKlGSMfFARLD0Oln24=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":95003},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=16"},"gitHead":"b298991650b29daf308ec8fb9452afbe4538a96b","scripts":{"test":"npm run test:unit","build":"rimraf dist && tsc","test:unit":"ts-node test/unit.ts","start:example":"ts-node example/app.module.ts","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"danqiusheng","email":"LZCNice2021@163.com"},"repository":{"url":"git+https://gitee.com/li-zucheng/nacos-plugin.git","type":"git"},"_npmVersion":"11.16.0","description":"NestJS Nacos 2.x configuration and service-discovery integration","directories":{},"_nodeVersion":"22.21.1","dependencies":{"js-yaml":"^4.3.0","nacos-config":"^2.6.3"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.2","nacos":"^2.6.3","rimraf":"^5.0.0","ts-node":"^10.9.0","typescript":"^5.4.0","@types/node":"^26.1.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","@nestjs/config":"^4.0.0","@types/js-yaml":"^4.0.9","@nestjs/testing":"^11.1.28","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","nacos":"^2.6.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/nest-nacos_1.1.0_1785548758969_0.9071759274655462","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@danqiusheng/nest-nacos","version":"1.2.0","description":"NestJS Nacos 2.x configuration and service-discovery integration","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"rimraf dist && tsc","test":"npm run test:unit","test:unit":"ts-node test/unit.ts","test:simple-e2e":"ts-node test/simple-options.e2e.ts","typecheck:example":"tsc --project tsconfig.example.json --noEmit","start:example":"ts-node example/app.module.ts","prepublishOnly":"npm run build && npm test && npm run typecheck:example"},"dependencies":{"js-yaml":"^4.3.0","nacos-config":"^2.6.3"},"peerDependencies":{"@nestjs/common":"^10.0.0 || ^11.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","nacos":"^2.6.0","reflect-metadata":"^0.2.0","rxjs":"^7.0.0"},"devDependencies":{"@nestjs/common":"^11.0.0","@nestjs/config":"^4.0.0","@nestjs/core":"^11.0.0","@nestjs/testing":"^11.1.28","@types/js-yaml":"^4.0.9","@types/node":"^26.1.2","nacos":"^2.6.3","reflect-metadata":"^0.2.2","rimraf":"^5.0.0","rxjs":"^7.8.2","ts-node":"^10.9.0","typescript":"^5.4.0"},"keywords":["nestjs","nest","nacos","service-discovery","config-center","configuration","microservice","load-balancing"],"repository":{"type":"git","url":"git+https://gitee.com/li-zucheng/nacos-plugin.git"},"bugs":{"url":"https://gitee.com/li-zucheng/nacos-plugin/issues"},"homepage":"https://gitee.com/li-zucheng/nacos-plugin","author":{"name":"danqiusheng"},"license":"MIT","engines":{"node":">=16"},"gitHead":"b298991650b29daf308ec8fb9452afbe4538a96b","_id":"@danqiusheng/nest-nacos@1.2.0","_nodeVersion":"22.21.1","_npmVersion":"11.16.0","dist":{"integrity":"sha512-m9Jh5Pe8nSoHCY2v5MgCYqWz8c0BU/qxtlNUlQZ8IjEgozY57KM+QrhIJVWVwVUkEreeasNRIiKNI3im/CW2FQ==","shasum":"436d79280c995ad99c9fc65ad9fe4158a6c049e1","tarball":"https://registry.npmjs.org/@danqiusheng/nest-nacos/-/nest-nacos-1.2.0.tgz","fileCount":45,"unpackedSize":108799,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIDaYtGWQSpzv9XA59clGdTvzkBIakxoJ4KipWjBK4is5AiEAvymw921s3Kodt0HTtsm6ay21LnwJr2BxsMoumtA5ASc="}]},"_npmUser":{"name":"danqiusheng","email":"LZCNice2021@163.com"},"directories":{},"maintainers":[{"name":"danqiusheng","email":"LZCNice2021@163.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/nest-nacos_1.2.0_1785551090242_0.28138051288536925"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-31T03:15:00.062Z","modified":"2026-08-01T02:24:50.512Z","1.0.0":"2026-07-31T03:15:00.409Z","1.1.0":"2026-08-01T01:45:59.124Z","1.2.0":"2026-08-01T02:24:50.384Z"},"bugs":{"url":"https://gitee.com/li-zucheng/nacos-plugin/issues"},"author":{"name":"danqiusheng"},"license":"MIT","homepage":"https://gitee.com/li-zucheng/nacos-plugin","keywords":["nestjs","nest","nacos","service-discovery","config-center","configuration","microservice","load-balancing"],"repository":{"type":"git","url":"git+https://gitee.com/li-zucheng/nacos-plugin.git"},"description":"NestJS Nacos 2.x configuration and service-discovery integration","maintainers":[{"name":"danqiusheng","email":"LZCNice2021@163.com"}],"readme":"# @danqiusheng/nest-nacos\n\n面向 NestJS 10/11 的 Nacos 2.x 配置中心与服务注册发现模块。\n\n## 1. 安装\n\n```bash\nnpm install @danqiusheng/nest-nacos nacos\n```\n\n## 2. 最简接入\n\n未开启 Nacos 鉴权时，只需要一个地址：\n\n```ts\nimport { Module } from '@nestjs/common';\nimport { NacosModule } from '@danqiusheng/nest-nacos';\n\n@Module({\n  imports: [NacosModule.forRoot('127.0.0.1:8848')],\n})\nexport class AppModule {}\n```\n\n这会同时启用配置中心和服务注册发现，默认使用：\n\n| 配置 | 默认值 |\n|---|---|\n| namespace | `public` |\n| group | `DEFAULT_GROUP` |\n| transport | HTTP OpenAPI |\n| 重试次数 | 3 次（包含第一次调用） |\n\n开启用户名密码鉴权时，也只需要配置一次连接信息：\n\n```ts\nNacosModule.forRoot({\n  serverAddr: '127.0.0.1:8848',\n  username: 'nacos',\n  password: 'nacos',\n});\n```\n\n`serverAddr` 支持集群地址数组：\n\n```ts\nNacosModule.forRoot({\n  serverAddr: ['10.0.0.11:8848', '10.0.0.12:8848'],\n  namespace: 'production',\n  username: process.env.NACOS_USERNAME,\n  password: process.env.NACOS_PASSWORD,\n  ssl: false,\n});\n```\n\n## 3. 使用环境变量\n\n插件不会隐式读取环境变量。推荐使用 Nest `ConfigModule` 明确传入配置：\n\n```ts\nimport { Module } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport { NacosModule } from '@danqiusheng/nest-nacos';\n\n@Module({\n  imports: [\n    ConfigModule.forRoot({ isGlobal: true }),\n    NacosModule.forRootAsync({\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        serverAddr: config.getOrThrow<string>('NACOS_SERVER_ADDR'),\n        namespace: config.get<string>('NACOS_NAMESPACE') ?? 'public',\n        username: config.get<string>('NACOS_USERNAME'),\n        password: config.get<string>('NACOS_PASSWORD'),\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n`.env` 示例：\n\n```dotenv\nNACOS_SERVER_ADDR=127.0.0.1:8848\nNACOS_NAMESPACE=public\nNACOS_USERNAME=nacos\nNACOS_PASSWORD=nacos\n```\n\n`forRootAsync` 同时支持 `useFactory`、`useClass` 和 `useExisting`。\n\n## 4. 自动注册当前服务\n\n在模块配置中加入 `instances`，应用启动时自动注册，关闭时自动注销：\n\n```ts\nNacosModule.forRoot({\n  serverAddr: '127.0.0.1:8848',\n  instances: [\n    {\n      serviceName: 'order-service',\n      ip: '192.168.1.20',\n      port: 3000,\n      weight: 1,\n      ephemeral: true,\n      metadata: { env: 'prod', version: '1.2.0' },\n    },\n  ],\n});\n```\n\n如果 IP 或端口来自环境变量，应使用 `forRootAsync` 构造 `instances`。\n\n## 5. 服务注册与发现\n\n### 注入服务\n\n```ts\nimport { Injectable } from '@nestjs/common';\nimport {\n  InjectNacosNaming,\n  NacosNamingService,\n} from '@danqiusheng/nest-nacos';\n\n@Injectable()\nexport class ServiceDiscovery {\n  constructor(\n    @InjectNacosNaming()\n    private readonly naming: NacosNamingService,\n  ) {}\n}\n```\n\n也可以直接使用构造器注入 `NacosNamingService`。\n\n### 手动注册和注销\n\n```ts\nconst instance = {\n  serviceName: 'payment-service',\n  ip: '192.168.1.30',\n  port: 3000,\n  metadata: { env: 'prod' },\n};\n\nawait this.naming.register(instance);\nawait this.naming.deregister(instance);\n```\n\n通过插件注册的实例会被记录，Nest 模块销毁时会自动注销。\n\n### 获取健康实例\n\n```ts\nconst instances = await this.naming.selectInstances('payment-service');\n```\n\n只选择一个实例：\n\n```ts\nconst instance = await this.naming.selectInstance('payment-service', {\n  strategy: 'roundRobin',\n  metadata: { env: 'prod' },\n});\n```\n\n支持三种策略：\n\n| strategy | 行为 |\n|---|---|\n| `random` | 随机选择，默认值 |\n| `roundRobin` | 按服务轮询 |\n| `weighted` | 按 Nacos 实例权重随机选择 |\n\n找不到满足健康状态和 metadata 条件的实例时，`selectInstance()` 会抛出异常。\n\n### 订阅服务变化\n\n```ts\nconst listener = (hosts: unknown[]) => {\n  console.log('payment-service 实例变化', hosts);\n};\n\nthis.naming.subscribe('payment-service', listener);\nthis.naming.unSubscribe('payment-service', listener);\n\n// 不传 listener：取消该服务的全部本地订阅\nthis.naming.unSubscribe('payment-service');\n```\n\n### 更新权重\n\n```ts\nawait this.naming.updateWeight(instance, 0.5);\n```\n\n## 6. 配置中心\n\n### 注入服务\n\n```ts\nimport { Injectable } from '@nestjs/common';\nimport {\n  InjectNacosConfig,\n  NacosConfigService,\n} from '@danqiusheng/nest-nacos';\n\n@Injectable()\nexport class SettingsService {\n  constructor(\n    @InjectNacosConfig()\n    private readonly nacos: NacosConfigService,\n  ) {}\n}\n```\n\n### 读取配置\n\n```ts\nconst raw = await this.nacos.getConfig('application.yaml');\n\nconst config = await this.nacos.getParsedConfig<{\n  server: { port: number };\n}>('application.yaml');\n```\n\n格式默认根据 `dataId` 后缀自动识别：\n\n| 后缀/format | 返回值 |\n|---|---|\n| `.json` / `json` | JSON 对象或数组 |\n| `.yaml`、`.yml` / `yaml` | YAML 解析结果 |\n| `.properties` / `properties` | 键值对象 |\n| 其他 / `txt` | 原始字符串 |\n\n强制按 JSON 解析：\n\n```ts\nconst config = await this.nacos.getConfigAsJson<AppConfig>(\n  'application.json',\n);\n```\n\n### 发布和删除配置\n\n```ts\nconst published = await this.nacos.publish(\n  'application.json',\n  'DEFAULT_GROUP',\n  JSON.stringify({ featureEnabled: true }),\n);\n\nconst removed = await this.nacos.remove(\n  'application.json',\n  'DEFAULT_GROUP',\n);\n```\n\n返回值为 Nacos SDK 的最终布尔结果。public namespace 删除已处理 `tenant=public` 导致的假成功问题。\n\n### 订阅热更新\n\n```ts\nconst listener = (raw: string, parsed?: unknown) => {\n  console.log('配置已更新', parsed);\n};\n\nthis.nacos.subscribe(\n  'application.yaml',\n  'DEFAULT_GROUP',\n  listener,\n);\n\nthis.nacos.unSubscribe(\n  'application.yaml',\n  'DEFAULT_GROUP',\n  listener,\n);\n```\n\n同一个 `dataId + group` 只建立一个底层 SDK 订阅，多个业务 listener 不会导致重复回调。\n\n## 7. 启动时预加载配置\n\n```ts\nNacosModule.forRoot({\n  serverAddr: '127.0.0.1:8848',\n  subscribeConfigs: [\n    { dataId: 'application.yaml', format: 'yaml' },\n    { dataId: 'database.properties', format: 'properties' },\n    {\n      dataId: 'optional.json',\n      required: false,\n      subscribe: false,\n    },\n  ],\n});\n```\n\n默认行为：\n\n- group 为 `DEFAULT_GROUP`。\n- format 为 `auto`。\n- `subscribe: true`，预加载后继续监听热更新。\n- `required: true`，读取失败会按启动失败策略处理。\n\n合并所有已声明配置：\n\n```ts\nconst merged = await this.nacos.getMergedConfig<AppConfig>();\n```\n\n对象会按声明顺序深度合并，后面的配置覆盖前面的同名字段。\n\n## 8. 本地缓存和启动失败策略\n\n```ts\nNacosModule.forRoot({\n  serverAddr: '127.0.0.1:8848',\n  subscribeConfigs: [{ dataId: 'application.yaml' }],\n  configLoading: {\n    failureMode: 'fallback',\n    cacheDir: '.nacos-cache',\n    retry: {\n      attempts: 3,\n      delayMs: 300,\n      backoffFactor: 2,\n      maxDelayMs: 3000,\n    },\n  },\n});\n```\n\n| failureMode | 行为 |\n|---|---|\n| `throw` | 读取必需配置失败时阻止应用启动，默认值 |\n| `fallback` | 优先读取本地缓存；无缓存且配置必需时阻止启动 |\n| `continue` | 记录警告并继续启动 |\n\n成功从 Nacos 读取配置后，插件会原子更新缓存文件。缓存目录应加入部署持久化目录，不建议提交到 Git。\n\n## 9. 健康检查\n\n`getServerStatus()` 和 `isHealthy()` 都是异步方法：\n\n```ts\nconst status = await this.naming.getServerStatus(); // UP | DOWN\n```\n\n可直接提供 Nest HTTP 健康接口：\n\n```ts\nimport { Controller, Get } from '@nestjs/common';\nimport { NacosHealthIndicator } from '@danqiusheng/nest-nacos';\n\n@Controller('health/nacos')\nexport class NacosHealthController {\n  constructor(private readonly health: NacosHealthIndicator) {}\n\n  @Get()\n  check() {\n    return this.health.isHealthy();\n  }\n}\n```\n\n返回：\n\n```json\n{ \"status\": \"up\" }\n```\n\n## 10. 高级连接配置\n\n绝大多数项目只需要顶层共享配置。只有 Naming 和 Config 使用不同地址、命名空间或云鉴权参数时，才使用嵌套配置：\n\n```ts\nNacosModule.forRoot({\n  serverAddr: 'shared-nacos:8848',\n  username: 'shared-user',\n  password: 'shared-password',\n\n  naming: {\n    serverList: 'naming-nacos:8848',\n    namespace: 'service-space',\n    appName: 'order-service',\n  },\n  config: {\n    serverAddr: 'config-nacos:8848',\n    namespace: 'config-space',\n    accessKey: process.env.NACOS_ACCESS_KEY,\n    secretKey: process.env.NACOS_SECRET_KEY,\n  },\n});\n```\n\n嵌套配置优先于顶层共享配置。只配置 `naming` 或只配置 `config`，可以只启用对应能力。\n\n## 11. 原始 SDK 客户端\n\n只有插件服务未覆盖 SDK 能力时才建议使用：\n\n```ts\nimport {\n  InjectNacosConfigClient,\n  InjectNacosNamingClient,\n} from '@danqiusheng/nest-nacos';\nimport type {\n  NacosConfigClient,\n  NacosNamingClient,\n} from 'nacos';\n\nconstructor(\n  @InjectNacosNamingClient()\n  private readonly namingClient: NacosNamingClient,\n  @InjectNacosConfigClient()\n  private readonly configClient: NacosConfigClient,\n) {}\n```\n\n原始客户端由模块统一关闭，不要在业务代码中重复调用 `close()`。\n\n## 12. 兼容性\n\n| 组件 | 支持范围 |\n|---|---|\n| NestJS | 10.x、11.x |\n| Node.js | >= 16 |\n| Nacos Node SDK | `nacos` 2.6.x |\n| Nacos Server | 2.x（已对 2.5.3 做真实端到端验证） |\n| Nacos Server 3.x | 不支持 |\n| 传输协议 | Nacos HTTP OpenAPI；不支持 gRPC |\n\n`nacos@2.6.x` 没有实现 Nacos 3.x 所需的 gRPC 协议。配置 `transport: 'grpc'` 时插件会立即抛出错误，不会静默回退造成假成功。\n\n插件本身不需要 Java。只有在本机运行 Nacos Server 时才需要 Java；NestJS 应用连接远程 Nacos 时只需要 Node.js。\n\n## 13. 常见问题\n\n### `getServerStatus()` 类型是 Promise\n\n必须使用 `await`：\n\n```ts\nconst status = await this.naming.getServerStatus();\n```\n\n### 鉴权后 Naming 正常但 Config 失败\n\n使用顶层 `username/password` 可以同时传给两个客户端，避免重复配置遗漏：\n\n```ts\nNacosModule.forRoot({ serverAddr, username, password });\n```\n\n### public namespace 应该怎么写\n\n省略 `namespace` 或设置为 `public` 都可以。插件会按 Naming 和 Config SDK 的不同要求正确归一化。\n\n### 应用无法退出\n\n请通过 Nest 的 `app.close()` 或正常进程信号关闭。模块销毁时会注销实例、取消订阅并关闭两个 SDK 客户端。\n\n### Nacos 3.x 为什么不能连接\n\n当前 Node SDK 没有 Nacos 3.x gRPC 实现。需要 Nacos 3.x 时不能通过修改 `transport` 参数解决，应等待或替换真正支持该协议的 SDK。\n\n## 14. 验证\n\n```bash\nnpm ci\nnpm run build\nnpm test\n```\n\n发布前还应针对真实 Nacos 2.x 执行端到端测试，并从服务端接口反查注册、配置发布和删除结果。\n","readmeFilename":"README.md"}