14 releases (5 breaking)
Uses new Rust 2024
| 0.6.2 | Oct 6, 2025 |
|---|---|
| 0.6.0 | Aug 20, 2025 |
| 0.5.1 | Jul 30, 2025 |
#74 in Configuration
1,145 downloads per month
160KB
2K
SLoC
apollo-rust-client
A robust Rust client for the Apollo Configuration Centre, with support for WebAssembly for browser and Node.js environments.
Features
- Multiple Configuration Formats: Support for Properties, JSON, YAML, Text formats with planned XML support
- Automatic Format Detection: Based on namespace file extensions
- Type-Safe Configuration Management: Compile-time guarantees and runtime type conversion
- Cross-Platform Support: Native Rust and WebAssembly targets
- Real-Time Updates: Background polling with configurable intervals and event listeners
- Comprehensive Caching: Multi-level caching with file persistence (native) and memory-only (WASM)
- Async/Await Support: Full asynchronous API for non-blocking operations
- Error Handling: Detailed error diagnostics with comprehensive error types
- Grayscale Release Support: IP and label-based configuration targeting
- Flexible Configuration: Direct instantiation or environment variable configuration
- Memory Management: Automatic cleanup with explicit control for WASM environments
Installation
Rust
Add the following to your Cargo.toml:
[dependencies]
apollo-rust-client = "0.6.2"
Alternatively, you can use cargo add:
cargo add apollo-rust-client
WebAssembly (npm)
npm install @qqiao/apollo-rust-client
Quick Start
Rust Usage
use apollo_rust_client::Client;
use apollo_rust_client::client_config::ClientConfig;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create client configuration
let client_config = ClientConfig {
app_id: "your_app_id".to_string(),
cluster: "default".to_string(),
config_server: "http://your-apollo-server:8080".to_string(),
secret: Some("your_apollo_secret".to_string()),
cache_dir: None, // Uses default: /opt/data/{app_id}/config-cache
label: None, // For grayscale releases
ip: None, // For grayscale releases
allow_insecure_https: None, // Allow self-signed certificates
#[cfg(not(target_arch = "wasm32"))]
cache_ttl: None, // Uses default: 600 seconds (10 minutes)
};
let mut client = Client::new(client_config);
// Start background polling for configuration updates
client.start().await?;
// Get configuration for different namespace formats
let namespace = client.namespace("application").await?;
// Handle different configuration formats
match namespace {
apollo_rust_client::namespace::Namespace::Properties(properties) => {
// Properties format (default) - key-value pairs
if let Some(app_name) = properties.get_string("app.name") {
println!("Application name: {}", app_name);
}
if let Some(port) = properties.get_int("server.port") {
println!("Server port: {}", port);
}
if let Some(debug) = properties.get_bool("debug.enabled") {
println!("Debug enabled: {}", debug);
}
}
apollo_rust_client::namespace::Namespace::Json(json) => {
// JSON format - structured data
#[derive(serde::Deserialize)]
struct Config {
name: String,
version: String,
}
let config: Config = json.to_object()?;
println!("Config: {} v{}", config.name, config.version);
}
apollo_rust_client::namespace::Namespace::Text(text) => {
// Text format - plain text content
println!("Text content: {}", text);
}
}
// Add event listener for configuration changes
client.add_listener("application", std::sync::Arc::new(|result| {
match result {
Ok(namespace) => println!("Configuration updated: {:?}", namespace),
Err(e) => eprintln!("Configuration update error: {}", e),
}
})).await;
Ok(())
}
Configuration via Environment Variables
use apollo_rust_client::client_config::ClientConfig;
// Initialize from environment variables
let client_config = ClientConfig::from_env()?;
Environment Variables:
APP_ID: Your application ID (required)APOLLO_CONFIG_SERVICE: The Apollo config server URL (required)IDC: The cluster name (optional, defaults to "default")APOLLO_ACCESS_KEY_SECRET: The secret key for authentication (optional)APOLLO_LABEL: Comma-separated list of labels for grayscale rules (optional)APOLLO_CACHE_DIR: Directory to store local cache (optional)APOLLO_CACHE_TTL: Time-to-live for cache in seconds (optional, defaults to 600, native targets only)APOLLO_ALLOW_INSECURE_HTTPS: Whether to allow insecure HTTPS connections (optional, defaults to false)
JavaScript/WebAssembly Usage
import { Client, ClientConfig } from "@qqiao/apollo-rust-client";
async function main() {
// Create client configuration
const clientConfig = new ClientConfig(
"your_app_id",
"http://your-apollo-server:8080",
"default"
);
// Set optional properties
clientConfig.secret = "your_apollo_secret";
clientConfig.label = "production";
clientConfig.ip = "192.168.1.100";
const client = new Client(clientConfig);
// Start background polling
await client.start();
// Get configuration cache
const cache = await client.namespace("application");
// Retrieve different data types
const appName = await cache.get_string("app.name");
const serverPort = await cache.get_int("server.port");
const debugEnabled = await cache.get_bool("debug.enabled");
const timeout = await cache.get_float("timeout.seconds");
console.log(`App: ${appName}, Port: ${serverPort}, Debug: ${debugEnabled}`);
// Add event listener for configuration changes
await cache.add_listener((data, error) => {
if (error) {
console.error("Configuration update error:", error);
} else {
console.log("Configuration updated:", data);
}
});
// IMPORTANT: Release memory when done
cache.free();
client.free();
clientConfig.free();
}
main().catch(console.error);
Configuration Formats
The library automatically detects configuration formats based on namespace names:
Properties Format (Default)
- Namespace:
"application","config.properties" - Format: Key-value pairs
- Example:
app.name = MyApp server.port = 8080
JSON Format
- Namespace:
"config.json","settings.json" - Format: Structured JSON data
- Example:
{"database": {"host": "localhost", "port": 5432}}
Text Format
- Namespace:
"readme.txt","content.txt" - Format: Plain text content
- Example: Raw text content
YAML Format
- Namespace:
"config.yaml","config.yml" - Format: Structured YAML data
- Example: Complex configuration structures with nested objects
#[derive(serde::Deserialize)]
struct AppConfig {
server: ServerConfig,
database: DatabaseConfig,
}
#[derive(serde::Deserialize)]
struct ServerConfig {
host: String,
port: u16,
}
#[derive(serde::Deserialize)]
struct DatabaseConfig {
host: String,
port: u16,
ssl: bool,
}
if let apollo_rust_client::namespace::Namespace::Yaml(yaml) = namespace {
let config: AppConfig = yaml.to_object()?;
println!("Server: {}:{}", config.server.host, config.server.port);
println!("Database: {}:{}", config.database.host, config.database.port);
}
Planned Formats
- XML:
"config.xml"
Configuration Options
ClientConfig Fields
app_id: Your application ID in Apollo (required)config_server: The Apollo config server URL (required)cluster: The cluster name (required, typically "default")secret: Optional secret key for authenticationcache_dir: Directory for local cache files (native only)- Default:
/opt/data/{app_id}/config-cache - WASM: Always
None(memory-only caching)
- Default:
label: Label for grayscale releases (optional)ip: IP address for grayscale releases (optional)
Error Handling
The library provides comprehensive error handling:
use apollo_rust_client::Error;
match client.namespace("application").await {
Ok(namespace) => {
// Handle successful configuration retrieval
}
Err(Error::Cache(cache_error)) => {
// Handle cache-related errors (network, parsing, etc.)
eprintln!("Cache error: {}", cache_error);
}
Err(Error::Namespace(namespace_error)) => {
// Handle namespace-related errors (format detection, etc.)
eprintln!("Namespace error: {}", namespace_error);
}
Err(e) => {
// Handle other errors
eprintln!("Error: {}", e);
}
}
Memory Management (WASM)
For WebAssembly environments, explicit memory management is required:
// Always call free() on WASM objects when done
cache.free();
client.free();
clientConfig.free();
This prevents memory leaks by releasing Rust-allocated memory on the WebAssembly heap.
Advanced Usage
Event Listeners
// Rust - Add event listener
client.add_listener("application", std::sync::Arc::new(|result| {
match result {
Ok(namespace) => println!("Config updated: {:?}", namespace),
Err(e) => eprintln!("Update error: {}", e),
}
})).await;
// JavaScript - Add event listener
await cache.add_listener((data, error) => {
if (error) {
console.error("Update error:", error);
} else {
console.log("Config updated:", data);
}
});
Grayscale Releases
let client_config = ClientConfig {
app_id: "my-app".to_string(),
config_server: "http://apollo-server:8080".to_string(),
cluster: "default".to_string(),
secret: None,
cache_dir: None,
label: Some("canary,beta".to_string()), // Multiple labels
ip: Some("192.168.1.100".to_string()), // Client IP
#[cfg(not(target_arch = "wasm32"))]
cache_ttl: None,
};
Custom JSON Deserialization
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
struct DatabaseConfig {
host: String,
port: u16,
ssl: bool,
}
if let apollo_rust_client::namespace::Namespace::Json(json) = namespace {
let db_config: DatabaseConfig = json.to_object()?;
println!("Database: {}:{}", db_config.host, db_config.port);
}
API Changes in v0.6.0
- Typed Namespaces: Support for multiple configuration formats with automatic detection
- Event Listeners: Real-time configuration change notifications
- Enhanced Error Handling: Comprehensive error types and better error reporting
- WASM Improvements: Better memory management and JavaScript interop
- Background Polling: Configurable automatic configuration refresh
Documentation
For comprehensive documentation, visit our wiki:
- Installation Guide - Setup instructions
- Rust Usage - Native Rust examples
- JavaScript Usage - WASM usage in JavaScript
- Configuration - Configuration options
- Features - Feature overview
- Error Handling - Error handling guide
- Design Overview - Architecture documentation
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the Apache License Version 2.0 - see the LICENSE file for details.
Dependencies
~14–33MB
~472K SLoC