A Rust CLI tool that converts environment variables into a JSON object validated against a JSON Schema.
Define your configuration structure using JSON Schema, set environment variables with a prefix, and get a valid JSON configuration — no manual parsing code required.
The tool transforms prefixed environment variables into a nested JSON object, then validates and auto-fixes type mismatches against your schema.
- Schema-driven validation — validates generated JSON against any valid JSON Schema
- Automatic type fixing — converts string values to match schema types (integers, booleans, arrays, etc.)
- Nested object support — use
_to create nested structures (e.g.,DB_HOST→db.host) - Array support — numeric path segments create array elements
- Stdin or file input — pipe a schema or provide a file path
Given a JSON Schema:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"age": { "type": "integer", "minimum": 0 }
}
}With these environment variables:
export PERSON_FIRST_NAME=John
export PERSON_LAST_NAME=Doe
export PERSON_AGE=30Run:
cat schema.json | env-to-schema-json --prefix PERSON_Output:
{
"first_name": "John",
"last_name": "Doe",
"age": 30
}cargo install env-to-schema-jsoncargo build --release
cp target/release/env-to-schema-json /usr/local/bin/env-to-schema-json --prefix <PREFIX> [schema.json]| Flag | Default | Description |
|---|---|---|
-p, --prefix |
PREFIX_ |
Prefix to filter environment variables |
-s, --schema |
(none) | Path to JSON schema file (omit to read from stdin) |
-d, --debug |
false |
Print the generated JSON before validation |
cat schema.json | env-to-schema-json --prefix MYAPP_env-to-schema-json --prefix MYAPP_ schema.jsonEnvironment variable names are transformed into JSON paths using these rules:
| Env Var | JSON Path |
|---|---|
APP_HOST=localhost |
app.host |
APP_DB__HOST=localhost |
app.db_host |
APP_SERVERS__0_HOST=a |
app.servers[0].host |
APP_SERVERS__1_HOST=b |
app.servers[1].host |
APP_SET_X-Forwarded-For=a |
app.set.x-forwarded-for |
- Underscores (
_) become dots (.) — creating nested objects - Double underscores (
__) become literal underscores (_) — for flat keys containing underscores - Numeric path segments create array elements
- All keys are converted to lowercase
- Every other character is carried through unchanged
The transformation produces . and _, but never -. A key that needs a dash
must contain one literally — __ escapes to an underscore, so
X__FORWARDED__FOR yields x_forwarded_for, a different key. This matters most
for HTTP header names:
| Env Var | JSON Key | |
|---|---|---|
..._X__FORWARDED__FOR_0 |
x_forwarded_for |
✗ not the header |
..._X-Forwarded-For_0 |
x-forwarded-for |
✓ |
A name containing a dash is not a valid shell identifier and cannot be
exported. Set it through a container runtime (Docker Compose's environment:
passes names straight through), or with env 'NAME=value' ... when testing
locally.
Indices must start at 0 and be contiguous. Setting ROUTES_2 without
ROUTES_0 and ROUTES_1 leaves unset elements in the array, and validation
fails with an error naming the variables involved.
# Run
cargo run -- --prefix PREFIX_
# Run tests
cargo test
# Run with schema file
cargo run -- --prefix CADDY_ --schema example/caddy-schema.json