env-schema

Crates.ioenv-schema
lib.rsenv-schema
version0.1.0
created_at2026-01-04 21:30:44.469289+00
updated_at2026-01-04 21:30:44.469289+00
descriptionEnvironment variable schema validation and management for Rust
homepage
repositoryhttps://github.com/eladbash/env-schema
max_upload_size
id2022517
size309,824
Elad Ben Shmuel (eladbash)

documentation

README

🔐 env-schema

Environment variable schema validation and management for Rust

Rust License

📖 What is env-schema?

env-schema provides a declarative approach to managing environment variables in Rust applications. Define your configuration schema once, and get validation, documentation generation, and type-safe loading for free.

✨ Why env-schema?

Three core benefits:

  1. 📋 Contract: Your schema is the single source of truth. Environment variables are validated against the schema at startup, catching configuration errors early.

  2. 📚 Documentation: Automatically generate .env.example files and Markdown documentation from your schema. Keep your docs in sync with your code.

  3. 🛡️ Strict Mode: Optionally enforce that only known environment variables are present, preventing typos and accidental configuration drift.

🚀 Quickstart (60 seconds)

📦 Add to your Cargo.toml:

[dependencies]
env-schema = "0.1.0"
env-schema-derive = "0.1.0"

📝 Define your configuration:

use env_schema::prelude::*;
use env_schema::resolve::LoadMode;
use env_schema::secret::Secret;
use env_schema_derive::EnvSchema;

#[derive(EnvSchema, Debug)]
struct AppConfig {
    /// Database connection URL
    #[env(required, secret, example = "postgres://user:password@localhost:5432/mydb")]
    database_url: Secret<String>,

    /// Server port number
    #[env(default = "3000", range(min = 1.0, max = 65535.0))]
    port: u16,

    /// Application environment
    #[env(default = "development", values = ["development", "staging", "production"])]
    environment: String,
}

▶️ Load and use:

fn main() -> Result<()> {
    let config = AppConfig::load(LoadMode::Lenient)?;
    
    println!("Database: {}", config.database_url.expose());
    println!("Port: {}", config.port);
    println!("Environment: {}", config.environment);
    
    Ok(())
}

✨ Features

✅ Validation

  • Required fields: Ensure critical configuration is present
  • Default values: Provide sensible defaults for optional settings
  • Type parsing: Automatic parsing to String, u16, u32, u64, i32, i64, bool, Option<T>, and custom types via FromStr
  • Range constraints: Validate numeric values within bounds
  • Value constraints: Restrict to specific allowed values
  • Regex patterns: Validate string formats
  • Strict mode: Reject unknown environment variables

🔒 Security

  • Secret wrapper: Secret<T> redacts sensitive values in Debug and Display output
  • Secret fields: Mark fields as secrets to prevent accidental logging

📚 Documentation Generation

  • .env.example files: Auto-generate example environment files
  • Markdown docs: Generate comprehensive configuration documentation
  • Comments and examples: Include field descriptions and example values

🛠️ Developer Experience

  • Derive macro: Define schemas using struct attributes
  • Builder API: Programmatically construct schemas
  • Clear error messages: Detailed validation errors with context
  • Aliases: Support legacy environment variable names
  • Deprecations: Warn when deprecated keys are used

🔗 Integrations & Comparisons

See INTEGRATIONS.md for:

  • 🔌 Integration examples with popular crates (dotenv, serde, config-rs, tokio, clap)
  • 📖 Migration guides from other environment variable crates
  • 📊 Comprehensive comparison table with alternatives

⚠️ Example Error Output

When validation fails, env-schema provides clear, actionable error messages:

Configuration validation failed with 3 error(s):

1. Missing required environment variable: `DATABASE_URL`
2. Value for `PORT` is out of range: `99999` (expected 1 to 65535)
3. Value for `ENVIRONMENT` does not match pattern `^(dev|staging|prod)$`: `invalid`

Errors are collected and reported together, so you can fix all issues in one pass.

📝 Generating Documentation

Generate documentation from your schema:

use env_schema_derive::EnvSchema;

#[derive(EnvSchema)]
struct AppConfig {
    #[env(required, secret, example = "postgres://localhost/db")]
    database_url: Secret<String>,
    
    #[env(default = "3000", doc = "Server port")]
    port: u16,
}

fn main() {
    let schema = AppConfig::schema();
    
    // Generate .env.example
    let env_example = schema.render_env_example();
    std::fs::write(".env.example", env_example).unwrap();
    
    // Generate Markdown documentation
    let docs = schema.render_docs_md();
    std::fs::write("CONFIG.md", docs).unwrap();
}

📄 Example .env.example Output

# Database connection URL
# Required
DATABASE_URL=<secret>

# Server port
PORT=3000

📋 Example Markdown Documentation

# Configuration

## How to Use

Set environment variables before running the application...

## Environment Variables

| Key | Required | Default | Example | Description | Constraints |
|-----|----------|---------|---------|-------------|-------------|
| `DATABASE_URL` | **Yes** | — | `<secret>` | Database connection URL | — |
| `PORT` | No | `3000` | — | Server port | Range: 1 to 65535 |

🔐 Security: Secret<T>

The Secret<T> wrapper type prevents accidental exposure of sensitive values:

use env_schema::secret::Secret;

let api_key = Secret("my-secret-key".to_string());

// Safe: redacts the value
println!("{:?}", api_key);  // prints: <redacted>
println!("{}", api_key);    // prints: <redacted>

// Explicit access when needed
let actual_key = api_key.expose();

⚠️ Important: Secret<T> only prevents accidental logging via Debug and Display. The value is still stored in memory. For production secrets, use proper secret management systems and never commit secrets to version control.

🎛️ Feature Flags

Enable optional features in Cargo.toml:

[dependencies]
env-schema = { version = "0.1.0", features = ["json", "url", "humantime"] }

Available Features

  • yaml (optional): Enables loading schemas from YAML files. Allows tools like envctl to consume schemas without needing Rust derive macros.

  • json (optional): Enables JSON error report rendering via report::render_json(). Useful for programmatic consumption of error reports in structured format.

  • url (optional): Enables parsing of Url types from the url crate. Allows environment variables to be parsed directly into url::Url types with validation.

  • humantime (optional): Enables parsing of Duration types using human-readable strings. Supports formats like "5s", "10m", "1h", "2d", or combinations like "1h30m15s".

📝 Loading Schema from YAML

You can load a schema from a YAML file or string, which is useful for tools that need to consume schemas without Rust code.

Enable YAML Support

Add the yaml feature to your Cargo.toml:

[dependencies]
env-schema = { version = "0.1.0", features = ["yaml"] }

YAML Format

A YAML schema can be defined with a top-level fields: array (preferred) or as a direct array:

fields:
  - key: "DB_URL"
    required: true
    secret: true
    doc: "Postgres connection string"
    example: "postgres://user:pass@host:5432/db"
    default: null
    aliases: ["DATABASE_URL"]
    deprecated: false
    range:
      min: 1
      max: 200
    values: null
    regex: null

  - key: "PORT"
    required: false
    default: "3000"
    doc: "Server port"
    range:
      min: 1
      max: 65535

Loading from YAML

use env_schema::schema::Schema;

// Load from a file
let schema = Schema::from_yaml_file("schema.yaml")?;

// Or load from a string
let yaml = r#"
fields:
  - key: "DB_URL"
    required: true
    secret: true
"#;
let schema = Schema::from_yaml_str(yaml)?;

// Use the schema as normal
let config = schema.load_map(LoadMode::Strict)?;

YAML Field Properties

  • key (required): The environment variable name (must be non-empty)
  • required (default: false): Whether the variable must be present
  • secret (default: false): Whether the variable contains sensitive data
  • doc (optional): Human-readable documentation
  • example (optional): Example value
  • default (optional): Default value (null means no default)
  • aliases (optional): Array of alternative variable names
  • deprecated (default: false): Whether the field is deprecated
  • deprecated_message (optional): Deprecation message (auto-generated if deprecated: true and message is empty)
  • range (optional): Numeric range constraint with min and max
  • values (optional): Array of allowed values (enum constraint)
  • regex (optional): Regular expression pattern constraint

Validation

The YAML loader performs comprehensive validation:

  • Duplicate keys are rejected
  • Empty keys are rejected
  • Aliases cannot equal the main key
  • Range constraints require min <= max
  • Values constraints must have at least one item
  • Regex patterns are compile-checked

All validation errors are aggregated and returned together for easy debugging.

See examples/schema.yaml for a complete example.

💡 Realistic Example

See examples/web_service.rs for a complete example with:

  • 🌐 Network configuration (listen address, base URL)
  • 🗄️ Database connection pooling
  • ⏱️ Timeout configuration
  • 🔀 CORS settings
  • 📊 Observability (Sentry, OpenTelemetry)

📄 License

Licensed under the MIT license (LICENSE-MIT)

Commit count: 0

cargo fmt