{"id":15643058,"url":"https://github.com/kaelzhang/ctrip-apollo","last_synced_at":"2025-04-14T10:10:26.548Z","repository":{"id":34471573,"uuid":"96731184","full_name":"kaelzhang/ctrip-apollo","owner":"kaelzhang","description":"The most delightful and handy Node.js client for ctrip apollo configuration service.","archived":false,"fork":false,"pushed_at":"2022-05-30T02:43:12.000Z","size":72,"stargazers_count":61,"open_issues_count":6,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-09T03:16:10.280Z","etag":null,"topics":["apollo","apollo-client","config-service","ctrip-apollo"],"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/kaelzhang.png","metadata":{"files":{"readme":"README.md","changelog":"HISTORY.md","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":"2017-07-10T03:14:14.000Z","updated_at":"2025-03-27T11:39:13.000Z","dependencies_parsed_at":"2022-08-28T10:50:32.628Z","dependency_job_id":null,"html_url":"https://github.com/kaelzhang/ctrip-apollo","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaelzhang%2Fctrip-apollo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaelzhang%2Fctrip-apollo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaelzhang%2Fctrip-apollo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaelzhang%2Fctrip-apollo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kaelzhang","download_url":"https://codeload.github.com/kaelzhang/ctrip-apollo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248662407,"owners_count":21141568,"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":["apollo","apollo-client","config-service","ctrip-apollo"],"created_at":"2024-10-03T11:58:45.862Z","updated_at":"2025-04-14T10:10:26.524Z","avatar_url":"https://github.com/kaelzhang.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/kaelzhang/ctrip-apollo.svg?branch=master)](https://travis-ci.org/kaelzhang/ctrip-apollo)\n[![Coverage](https://codecov.io/gh/kaelzhang/ctrip-apollo/branch/master/graph/badge.svg)](https://codecov.io/gh/kaelzhang/ctrip-apollo)\n\u003c!-- optional appveyor tst\n[![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/kaelzhang/ctrip-apollo?branch=master\u0026svg=true)](https://ci.appveyor.com/project/kaelzhang/ctrip-apollo)\n--\u003e\n\u003c!-- optional npm version\n[![NPM version](https://badge.fury.io/js/ctrip-apollo.svg)](http://badge.fury.io/js/ctrip-apollo)\n--\u003e\n\u003c!-- optional npm downloads\n[![npm module downloads per month](http://img.shields.io/npm/dm/ctrip-apollo.svg)](https://www.npmjs.org/package/ctrip-apollo)\n--\u003e\n\u003c!-- optional dependency status\n[![Dependency Status](https://david-dm.org/kaelzhang/ctrip-apollo.svg)](https://david-dm.org/kaelzhang/ctrip-apollo)\n--\u003e\n\n# ctrip-apollo\n\nThe most delightful and handy Node.js client for Ctrip's [apollo](https://github.com/ctripcorp/apollo) configuration service, which\n\n- **Provides easy-to-use APIs** that just leave everything else to `ctrip-apollo`\n- **Implements update notifications** by using HTTP long polling, and handles all kinds of network errors.\n- **Updates configurations in background** that we could get a specific config property with a synchronous method with **NO** performance issues.\n- **Supports custom retry policy for polling**\n- **Implements disk cache** to against the situation that all config services are down.\n- **Supports both http and https requests**\n- **Supports configurations in both properties format and JSON format**\n\n`ctrip-apollo` directly uses `async/await` and requires node \u003e= 7.10.1\n\n### Related NPM Package: [`apollo-declare`](https://github.com/kaelzhang/apollo-declare)\n\nFor some scenarios, we want to:\n\n- declare a certain config key before we use it which makes sure that we never use a key that does not exist in apollo config service.\n- fallback to a base namespace if the config key is not defined in current namespace.\n\nThen you can try [`apollo-declare`](https://github.com/kaelzhang/apollo-declare)\n\n## Install\n\n```sh\n$ npm i ctrip-apollo\n```\n\n## Usage\n\n```js\nconst apollo = require('ctrip-apollo')\n\nconst app = apollo({\n  host: 'http://localhost:8070',\n  appId: '100004458'\n})\n\n// Get the default namespace\nconst namespace = app.namespace()\n// Namespace is an EventEmitter of nodejs\n.on('change', ({\n  key,\n  oldValue,\n  newValue\n}) =\u003e {\n  // Updates new values to `process.env`\n  process.env[key] = newValue\n})\n\n// - Fetch configurations for the first time\n// - start update notification polling (by default)\nnamespace.ready()\n.then(() =\u003e {\n  console.log(namespace.get('portal.elastic.cluster.name'))\n  // 'hermes-es-jp'\n\n  // THen, go and change/publish configurations in apollo admin\n  console.log(namespace.get('portal.elastic.cluster.name'))\n  // 'hermes-es-us'\n  // \u003c----\n  // ctrip-apollo handles update notifications in the background\n\n  console.log(process.env['portal.elastic.cluster.name'])\n  // 'hermes-es-us'\n})\n```\n\n### Initialize with cluster and namespace\n\n```js\nconst ns = apollo({\n  host,\n  appId,\n\n  // Save local cache to the current directory\n  cachePath: __dirname\n})\n.cluster('my-cluster')\n.namespace('my-namespace')\n\nconst start = async () =\u003e {\n  // We can also use async/await\n  await ns.ready()\n\n  console.log(ns.config())\n  // {\n  //   'portal.elastic.document.type': 'biz',\n  //   'portal.elastic.cluster.name': 'hermes-es-fws'\n  // }\n}\n\nstart()\n```\n\n### Disable update notifications(HTTP long polling)\n\nand enable fetching on every 3 minutes\n\n```js\nconst app = apollo({\n  host,\n  appId,\n  enableUpdateNotification: false,\n  enableFetch: true,\n  fetchInterval: 3 * 60 * 1000\n})\n\nclient.ready()\n.then(() =\u003e {\n  console.log(client.get('portal.elastic.cluster.name'))\n  // might be:\n  // 'hermes-es-us'\n})\n```\n\n### Chainable\n\n```js\nconst ns = await\n\napollo({host, appId})               // \u003c-- app\n\n  .cluster('ap-northeast-1')        // \u003c-- cluster\n  .enableUpdateNotification(true)   // \u003c-- cluster\n\n    .namespace('account-service')   // \u003c-- namespace\n    .enableFetch(false)             // \u003c-- namespace\n    .ready()                        // \u003c-- Promise {namespace}\n\nconsole.log(ns.get('account.graphql.cluster.name'))\n// might be:\n// graph-us-3\n```\n\n### Configurations in JSON format\n\nBy default, `ctrip-apollo` and apollo admin service store configurations in `java.util.Properties` format.\n\nIf we have two namespaces.\n\nThe one is `account-service` which stores configurations in JSON,\n\n```json\n{\n  \"redis.sentinel.port\": 6379\n}\n```\n\nwhile the other one `asset-service` in properties\n\n```properties\nredis.sentinel.port=6379\n```\n\n```js\nconst cluster = apollo({host, appId}).cluster('ap-northeast-1')\n\n// in JSON format\ncluster.namespace('account-service', 'JSON') // \u003c=====\n.ready()\n// .ready() resolves the namespace instance\n.then(account =\u003e {\n  console.log(account.get('redis.sentinel.port')) // 6379\n})\n\ncluster.namespace('asset-service')\n.ready()\n.then(asset =\u003e {\n  console.log(asset.get('redis.sentinel.port'))   // '6379'\n})\n```\n\n# APIs\n\n```\n  app (client)                           config service\n    |                                        | | |\n    |-- cluster  \u003c----- long polling --------| | |\n    |   |                                      | |\n    |   |-- namespace  \u003c------- fetch ---------| |\n    |                                            |\n    |-- cluster  \u003c-------- long polling ---------|\n```\n\n## app `ApolloApplication`\n\nAn application can have one or more clusters\n\n### apollo(options): ApolloApplication\n\n- **options** `Object`\n\nEssential options:\n  - **host** `URL::host` the host (including protocol, hostname and port) of the apollo config service\n  - **appId** `string` apollo application id\n\nOptional options:\n  - **enableUpdateNotification?** `boolean=true` set to `false` to disable update notification.\n  - **pollingRetryPolicy?** [`PollingRetryPolicy=apollo.DEFAULT_POLLING_RETRY_POLICY`](#pollingretrypolicy)\n  - **enableFetch?** `boolean=false` set to `true` to enable the feature\n  - **fetchTimeout** `number=0` timeout in milliseconds before the initial fetch or interval fetch result in an `FETCH_TIMEOUT` error.\n  - **fetchInterval?** `number=5 * 60 * 1000` interval in milliseconds to pull the new configurations. Defaults to `5` minutes. Setting this option to `0` will disable the feature.\n  - **fetchCachedConfig?** `boolean=true` whether refresh configurations by fetching the restful API with caches. Defaults to `true`. If you want to always fetch the latest configurations (not recommend), set the option to `false`\n  - **cachePath?** `path` specify this option to enable the feature to save configurations to the disk\n  - **skipInitFetchIfCacheFound?** `boolean=false` whether a namespace should skip the initial fetching if the corresponding cache is found on disk.\n\nReturns `ApolloApplication`\n\n \u003c!-- and class `ApolloClient` is a subclass of [`EventEmitter`](https://nodejs.org/dist/latest-v11.x/docs/api/events.html#events_class_eventemitter). --\u003e\n\n#### options.enableUpdateNotification\n\nIf `options.enableUpdateNotification` is set to `true`(the default value), all clusters will start to receive update notification automatically.\n\nMake sure the timeout of your gateway is configured more than 60 seconds, [via](https://github.com/ctripcorp/apollo/wiki/%E5%85%B6%E5%AE%83%E8%AF%AD%E8%A8%80%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97#142-http%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E)\n\n#### options.enableFetch\n\nIf `options.enableFetch` is set to `true` (default value is `false`), all namespaces will fetch new configurations on every time period of `options.fetchInterval`\n\n#### options.skipInitFetchIfCacheFound\n\n- **`true`** the client tries to read local cache first, if the cache is found, then it will skip the initial fetching.\n- **`false`** the client tries to fetch configuration from config service first, if it fails, it will try to read the local cache\n\nIf neither reading local cache nor fetching from remote comes successful, `await namespace.ready()` will fail.\n\n### app.cluster(clusterName?): ApolloCluster\n\n- **clusterName?** `string='default'` the cluster name. The cluster name defaults to `'default'`\n\nCreates a new cluster under the current application.\n\nReturns `ApolloCluster`\n\n### app.namespace(namespaceName?, type?): ApolloNamespace\n\n- **namespaceName?** `string='application'` the optional namespace name which defaults to `'application'` which is the default namespace name of Ctrip's Apollo config service.\n- **type?** `enum\u003cPROPERTIES|JSON\u003e=PROPERTIES` the type of the configuration format syntax. Defaults to `'PROPERTIES'`\n\nCreate a new namespace under the default cluster of the current application\n\nReturns `ApolloNamespace`.\n\n## cluster `ApolloCluster`\n\nA cluster can have one or more namespaces. And all namespaces of the same cluster use a single polling manager to receive update notifications.\n\n### cluster.namespace(namespaceName?): ApolloNamespace\n\n- **namespaceName?** `string='application'`\n\nCreates a new namespace of the current cluster.\n\nReturns `ApolloNamespace`\n\n### cluster.enableUpdateNotification(enable): this\n\n- **enable** `boolean`\n\nEnable or disable the updateNotification. Returns `this`\n\n```js\n// Get the default cluster, which is equivalent to\n// app.cluster('default')\nconst cluster = app.cluster()\n\n// Disable update notification\ncluster.enableUpdateNotification(false)\n```\n\n## namespace `ApolloNamespace`\n\nA namespace is what a configuration key belongs to.\n\n### await namespace.ready(): this\n\nFetch the configuration from config service for the first time, or fallback to reading the local cache file, and if `options.skipInitFetchIfCacheFound` is set to `false` (the default value).\n\nIf it fails to fetch configurations and read local cache, this method will reject.\n\n**MAKE SURE** we await `namespace.ready()` before any `namespace.config()` or `namespace.get()` methods are invoked.\n\n### namespace.config(): Object\n\nReturns `Object` the shallow copy of all configurations for the current namespace / application.\n\nNotice that, all configuration getters of a namespace are synchronous methods\n\n```js\nconsole.log('application config', namespace.config())\n```\n\n### namespace.get(key): string\n\n- **key** `string` config key name\n\nReturns the config value of the corresponding key `key`\n\n```js\nconsole.log('config for host', namespace.get('host'))\n```\n\n### namespace.has(key): boolean\n\n\u003e new in 4.4.0\n\nCheck if a key is in the config.\n\n### namespace.enableFetch(enable): this\n\n- **enable** `boolean`\n\nEnable or disable fetching in every `options.fetchInterval`\n\n## Getters\n\n### Getter: namespace.namespace\n\nReturns `string` the namespace name\n\n### Getter: namespace.cluster\n\nReturns `string` name of the cluster which the current namespace belongs to\n\n### Getter: cluster.cluster\n\nReturns `string` the cluster name\n\n## Namespace Events\n\n### Event: `'change'`\n\n- **e.key** `string` key of the configuration that has been changed\n- **e.oldValue** `any` the old value of the configuration\n- **e.newValue** `any` the new value of the configuration that has been set.\n\nEmits if the any configuration changes.\n\nBy default, `ctrip-apollo` uses HTTP long polling to listen the changes of the configurations.\n\n```js\nnamespace.on('change', e =\u003e {\n  console.log('key', e.key)\n  console.log('oldValue', e.oldValue)\n  console.log('newValue', e.newValue)\n})\n```\n\n### Event: `'add'`\n\n- **e.key** `string`\n- **e.value** `any` the value of the newly added configuration\n\nEmits if a new configuration key has been added\n\n### Event: `'delete'`\n\n- **e.key** `string`\n- **e.value** `any` the value of the configuration that has been deleted.\n\nEmits if a configuration key has been deleted\n\nIf `options.fetchInterval` is set to `0` and `options.updateNotification` is set to `false`, then the event will never emit.\n\n### Event: `'updated'`\n\n\u003e Added in 4.2.0, event data added in 4.5.0\n\n- **e.oldConfig** `object` the old config\n- **e.newConfig** `object` the new config after updated\n\nEmits after all changes, adding and deletion of the current updation have been processed.\n\n### Event: `'fetch-error'`\n\nEmits if it fails to fetch configurations\n\n### Event: `'save-error'`\n\nEmits if it fails to save configurations to local cache file\n\n### PollingRetryPolicy\n\n`PollingRetryPolicy` is to tell the system what to do next if an error occured when receiving update notifications.\n\n```ts\ntype PollingRetryPolicy = Function(retries): PollingRetryDirective\n```\n\n- **retries** `number` how many times has retried till the last error.\n\n```ts\ninterface PollingRetryDirective {\n  // `abandon: true` ignores all properties below\n  // and stops update notification polling which is a dangerous directive.\n  abandon?: boolean\n  // After `delay` milliseconds,\n  // it will try to poll the update notification again\n  delay?: number\n  // Tells the system to reset the `retries` counter.\n  // And the `retries` counter always reset to zero if it receives a sucessful notification\n  reset?: boolean\n}\n\n// If there is no properties set, it will retry immediately.\n```\n\nThe default `pollingRetryPolicy` is equivalent to:\n\n```js\n// It schedule the first retry in 10 seconds, and adds extra 10s delay everytime.\n// It will reset the `retries` counter after the 6th retry.\nconst {DEFAULT_POLLING_RETRY_POLICY} = require('ctrip-apollo')\n```\n\nAnd we can define our own policy\n\n```js\nconst pollingRetryPolicy = retries =\u003e ({\n  // Stop polling after 11(the first and the latter 10 retries) errors\n  abandon: retries \u003e= 10,\n  // Longer and longer delays\n  delay: retries * 10 * 1000\n  // And no reset\n})\n\nconst app = apollo({\n  ...,\n  pollingRetryPolicy\n})\n```\n\n### apollo.AVAILABLE_OPTIONS `Array`\n\nList of available option keys.\n\n### Error Codes\n\nSee [error.js](https://github.com/kaelzhang/ctrip-apollo/blob/master/src/error.js) for details\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaelzhang%2Fctrip-apollo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkaelzhang%2Fctrip-apollo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaelzhang%2Fctrip-apollo/lists"}