Reference
Rust SDK
Add the fluxgate crate to evaluate flags from async Rust, keep a user's flags in memory with a snapshot, or use the blocking client.
Before you start
- Rust 1.85 or newer. The async client runs on Tokio.
- The edge server is running and
express-checkoutis deployed to Development. See Clients and edge server. - The SDK key of a Backend client, in the story
checkout-service, for example in theFLUXGATE_SDK_KEYenvironment variable.
Crate
| Crate | Links |
|---|---|
fluxgate | crates.io, API docs on docs.rs, source |
The current version is 1.0.0. Optional parts are cargo features:
| Feature | Default | Adds |
|---|---|---|
rustls | yes | TLS through rustls. |
native-tls | no | TLS through the operating system's library. Use it with default-features = false. |
blocking | no | fluxgate::blocking, a synchronous client for code without an async runtime. |
openfeature | no | fluxgate::openfeature::FluxGateProvider, a provider for the open-feature crate. |
Evaluate a flag
[dependencies]
fluxgate = "1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }use fluxgate::{EvaluationContext, FluxGateClient};
#[tokio::main]
async fn main() -> Result<(), fluxgate::ConfigError> {
let fluxgate = FluxGateClient::new(
"http://localhost:8081",
&std::env::var("FLUXGATE_SDK_KEY").expect("set FLUXGATE_SDK_KEY"), // <clientId>.<apiKey>
)?;
let user = EvaluationContext::new("user-1")
.with("country", "CA")
.with("user_tier", "plus");
let variant = fluxgate.get_string("express-checkout", "classic", &user).await;
println!("checkout: {variant}");
let details = fluxgate.evaluate("express-checkout", "classic".to_owned(), &user).await;
println!("{:?} {} {:?}", details.variant, details.reason, details.error_code);
Ok(())
}checkout: express
Some("express") TARGETING_MATCH NoneFluxGateClient::new fails only on a malformed base URL or SDK key. The client is cheap to clone, and clones share the connection pool and the cache, so create one and pass it around.
| Method | Returns |
|---|---|
is_enabled(key, &ctx) | true only when a boolean flag is on |
get_bool, get_string, get_i64, get_f64, get_value (JSON) | The value, or your default |
evaluate(key, default, &ctx) | FlagEvaluation<T>: value, variant, reason, error_code, error_message, metadata |
evaluate_all(&ctx, version) | Every flag in one call, as Result<BulkEvaluation, Error> |
watch(ctx) | A FlagSnapshot with every flag for one context, polled every 30 seconds |
is_healthy() | true when the edge answers GET /health with 2xx |
To change the defaults, use the builder:
let fluxgate = FluxGateClient::builder("http://localhost:8081", sdk_key)
.timeout(Duration::from_secs(2)) // default 5 s
.max_attempts(2) // default 3
.initial_backoff(Duration::from_millis(50)) // default 100 ms
.cache_ttl(Duration::from_secs(10)) // default 30 s
.build()?;Keep a user's flags in memory
A FlagSnapshot loads every flag for one context in one bulk call and polls with If-None-Match. Reads are synchronous and make no network call, which suits code that reads many flags for the same user:
let flags = fluxgate.watch(EvaluationContext::new("user-1").with("country", "CA"));
flags.ready().await;
let checkout = flags.get_string("express-checkout", "classic");
let banner = flags.is_enabled("holiday-banner");
// Switch to another user, for example after sign-in.
flags.set_context(EvaluationContext::new("user-2").with("country", "US")).await;flags.subscribe() returns a tokio::sync::watch::Receiver that changes whenever a flag or the status changes. Polling stops when the last clone of the snapshot is dropped, or when you call close(). Use watch_with_interval to poll at another interval; Duration::ZERO turns polling off.
Without an async runtime
[dependencies]
fluxgate = { version = "1", features = ["blocking"] }use fluxgate::EvaluationContext;
use fluxgate::blocking::FluxGateClient;
fn main() {
let fluxgate = FluxGateClient::new("http://localhost:8081", &std::env::var("FLUXGATE_SDK_KEY").unwrap()).unwrap();
let user = EvaluationContext::new("user-1").with("country", "CA").with("user_tier", "plus");
println!("checkout: {}", fluxgate.get_string("express-checkout", "classic", &user));
}The blocking client runs the async client on its own runtime with one worker thread, which also polls snapshots. Do not call it from inside an async runtime: Tokio panics on blocking calls there.
OpenFeature
[dependencies]
fluxgate = { version = "1", features = ["openfeature"] }
open-feature = "0.3"use fluxgate::openfeature::FluxGateProvider;
use open_feature::{EvaluationContext, OpenFeature};
let mut api = OpenFeature::singleton_mut().await;
api.set_provider(FluxGateProvider::new(fluxgate)).await;
let client = api.create_client();
drop(api);
let user = EvaluationContext::default()
.with_targeting_key("user-1")
.with_custom_field("country", "CA")
.with_custom_field("user_tier", "plus");
let variant = client
.get_string_value("express-checkout", Some(&user), None)
.await
.unwrap_or_else(|_| "classic".to_owned());Date-time context fields are sent as RFC 3339 strings. OpenFeature providers in Rust receive no default value, so a flag that is off resolves to an error for every type except bool, and OpenFeature returns your default.
Good to know
- Evaluations never fail.
error_codeis one ofFlagNotFound,TypeMismatch,TargetingKeyMissing,InvalidContext,ProviderNotReady,ParseError,UnauthorizedandGeneral. - The default sets the expected type:
bool,String,i64,f64orserde_json::Value.Value::Nullaccepts any flag. Ani64read accepts only integers; anf64read accepts any number. - A flag that is off returns your default, with the edge's reason and no error.
Debugoutput of the client and its builder never includes the SDK key.
Try the samples
The repository has a CLI sample, a blocking sample and an axum web service. samples/docker-compose.yml starts a separate FluxGate stack on ports 38000 (admin UI), 38080 (backend) and 38081 (edge), and samples/setup.sh creates a Backend client, a boolean flag and a contextual flag. Follow samples/README.md.
Next
For the other languages, see Java and Spring Boot and TypeScript, React and Angular. The HTTP calls behind every SDK are in the Edge server API.