Nacos access example
This example demonstrates how to integrate Nacos in VextJS to implement service registration and discovery, dynamic configuration during runtime, and remote configuration patch during startup.
VextJS provides the official Nacos plug-in @devcodex/nacos, which encapsulates the registration/discovery and runtime configuration subscription processes. For content that must take effect before the framework configuration is frozen (such as database configuration), the bootstrap config provider of src/config/bootstrap.ts should be used.
Recommended to use in layers:
- Service registration/discovery, dynamic switch during runtime: directly use the official plug-in
@devcodex/nacos - Boot database/key/infrastructure configuration: Use
src/config/bootstrap.tsto pull Nacos configuration and return patch :::
Preconditions
- Prepare a Vext TypeScript API project with
dev,build, andstartscripts from Quick Start. - The current framework requires Node.js
^20.19.0 || >=22.12.0 - Prepare a reachable Nacos server and verify the namespace's actual ID, group, authentication, and client network. The plugin defaults to the public namespace; configure a different deployed ID explicitly.
- The plugin checked on 2026-09-25 was
@devcodex/nacos@0.2.10. Its Vext peer range is>=0.3.4, and its internal JavaScript SDK isnacos@2.6.3. The SDK version is not the Nacos Server version. This page does not claim every server version has been verified. Check the plugin release information and your actual environment.
1. Recommendation: Use the @devcodex/nacos official plug-in
1. Installation
2. Configuration (src/config/default.ts)
Description:
configis suitable for single configuration scenariosconfigsis suitable for basic configuration + environment coverage configuration split- When both exist, the merge order is
config -> configs[0] -> configs[1] ...; later objects win, while arrays replace as a whole. This first example uses onlyconfig. - Explicit plugin parameters and
app.config.nacosmerge shallowly; passingservicereplaces the entire service object. - The server decides whether authentication is enabled. Do not infer a default from a "2.x" label; see the Nacos authentication docs.
3. Register plug-in (src/plugins/nacos.ts)
4. Read a feature flag
Nacos config must be a JSON object. This route enables only a flag whose
value is strictly true; a wrong type does not count as enabled. The plugin
keeps the top-level remoteConfig object reference and updates its fields
in place. The property may not yet exist when the first pull fails, so read
it through req.app at request time with a default. A cached nested-object
reference is not guaranteed to refresh after an update.
5. Start and verify
In the Nacos console, create dataId order-service in the selected
namespace and DEFAULT_GROUP with this JSON:
From the application directory:
Expect HTTP 200 and data.enabled: true in the default wrapper. Change the
remote field to false, wait for the subscription update log, and request
again; expect false. An unknown key defaults to false. The console should
show an order-service instance at port 3000. Stop dev and verify the
production path:
After stopping the app, confirm the instance is unregistered. A registered
service address must be reachable by consumers; 127.0.0.1 is only for a
local demonstration. Successful registration during startup does not mean
the HTTP listener is ready; arrange readiness and load-balancer draining in
deployment. Verify production config, authentication, and network access in
the actual Nacos environment.
2. Extended configuration and service discovery
Explicit plugin options
Explicit parameter passing is also supported (overriding app.config.nacos):
Dynamic port (consistent with app.config.port)
service.port in config/default.ts is a static value and the final merged port number cannot be read.
If the ports of each environment are different (such as sit: 10019 / prod: 20019), it is recommended to dynamically inject it in the plug-in:
In this way, service.port in config/default.ts is only a type placeholder, and the actual registered port is determined by app.config.port.
Each environment only needs to set port: 10019 in the corresponding config file, and nacos will automatically follow.
Use src/config/bootstrap.ts for startup remote config
If you want to pull the database configuration from Nacos before MonSQLize is initialized, do not put this step in a normal plug-in; it is recommended to use createNacosBootstrapProvider() provided by @devcodex/nacos directly:
Prepare config.json in the db-config group. Its root must directly use
the Vext config shape, such as database, without an extra remoteConfig
wrapper. The provider closes its client after pulling and does not keep a
subscription. With required: true, pull, parse, or timeout failures block
startup; an initial pull failure in the ordinary runtime plugin only logs
a warning. The priority is default < config profile < local < provider < CLI, with local loaded only in development/test; see
Configuration.
:::info current boundary
createNacosBootstrapProvider() is only responsible for batch pulling and deep merging of JSON object patches during the startup period, which is suitable for content such as databases, keys, and infrastructure configurations that "must take effect before the configuration is frozen."
The result enters the app.config provider patch merge and does not
automatically become app.remoteConfig.
It is not responsible for:
- Service registration
- Service discovery
app.nacosmountapp.remoteConfiginjection and runtime subscription update
These runtime capabilities are still handled by nacosPlugin().
If you need:
- Service registration/service discovery
- Consistent
app.remoteConfigbehavior with plugins - Continuous subscription updates after configuration changes
All should continue to be processed using nacosPlugin() in src/plugins/nacos.ts instead of completing it in the bootstrap phase.
Service discovery boundary during bootstrap
You cannot directly call app.nacos!.discover() there by default.
The reason is that src/config/bootstrap.ts runs before Vext App is created:
- There is no
appat this time nacosPlugin()has not been executed yet- Therefore
app.nacos/app.remoteConfigdoes not exist
So the recommended bounds are:
- Only configuration patch pulling is done during startup →
createNacosBootstrapProvider() - Runtime service registration/service discovery/configuration subscription →
nacosPlugin()
Use app.remoteConfig for runtime configuration
If configuration affects only runtime feature flags, gradual rollout settings, or external API addresses, and is not needed to initialize database, plugins, or middlewares, use the configuration subscription provided by @devcodex/nacos:
- After initial startup, the plug-in will pull the Nacos configuration and mount it to
app.remoteConfig - Subsequent configuration changes will automatically update
app.remoteConfig - No need to restart the service
Actual behavior depends on what is configured:
enabled: false, noserverAddr, or neither a config source nor a service skips initialization and does not mount related extensions.- Configuring
servicecreates the Naming Client, registers the instance, and mountsapp.nacos. A config-only subscription cannot calldiscover. - Configuring
configorconfigspulls and subscribesapp.remoteConfig. Invalid JSON or non-object changes warn and keep that source's last valid version; empty content removes that source. - With both enabled, close in LIFO order deregisters the instance and closes the Naming Client, then closes the Config Client. Deregistration failure logs a warning; a completed call does not prove the server confirmed it.
- Importing the package augments VextApp/VextConfig types but does not mean
runtime initialization happened.
app.configremains frozen; dynamic config does not rebuild the database or rate limit middleware.
Use service discovery
This advanced Service assumes another user-service is registered and
offers /api/users/:id. An application route may call
app.services.user.getUser(id). discover throws if no healthy instance
exists and returns an HTTP URL. selectInstances can return a list, but
the caller must implement weighting or consistent-hash selection.
Multiple runtime configurations
This approach is suitable for:
- Feature flags and environment-specific overrides
- Shared service settings with tenant or region overrides
- Gradual rollout settings updated during runtime
3. Runtime boundaries and troubleshooting
Service discovery cache for frequent calls
discover calls the Naming Client's selectInstances each time.
Whether that reaches the network depends on the SDK's instance cache and
subscription state. Add an application cache only when measurement shows it
is needed. This single-app, default-group snippet demonstrates a TTL:
Caching one URL pins traffic to one instance for the TTL and may keep calling a removed node. Clear and rediscover after failure. With multiple apps, namespaces, or groups, isolate caches and include those dimensions in their keys; do not share this name-only Map.
Dependency diagnostic endpoint
This independent route checks the Nacos dependency. It does not replace the
framework's built-in /health:
Request /nacos-status; 503 means this example's dependency check failed.
The query may use the SDK cache, so 200 does not prove a real-time probe of
the Nacos server.
Nacos config data format
Create JSON config in the console:
Subscription updates only change app.remoteConfig. Application code
must read those values when used; they do not automatically change initialized
framework settings such as app.config.rateLimit.
4. Next step
@devcodex/nacosnpm package — API docs and changelog.- OpenTelemetry integration example — observability.
- Plugin system — custom
definePlugin()extensions. - app.fetch — built-in HTTP client, timeouts, retries, and request ID propagation.