blast docs
blast is a config-driven API load tester and mock server written in Rust. Define your endpoints in blast.config.json, then hit every one with a single command — no code, no scripting.
It supports fake data generation via {{fake.*}} placeholders, request chaining using JSON extraction and dot-path rules, fixed-RPS load tests, and stress ramp tests that auto-detect where your API breaks.
Installation
Linux & macOS
curl -fsSL https://raw.githubusercontent.com/Walon-Foundation/blast/main/install.sh | shDetects your OS and architecture. Downloads the pre-built binary to ~/.local/bin. See the install guide for custom install paths.
Windows (PowerShell)
irm https://raw.githubusercontent.com/Walon-Foundation/blast/main/install.ps1 | iexInstalls the x86_64-pc-windows-msvc binary and adds it to your user PATH.
Build from source
cargo install --git https://github.com/Walon-Foundation/blastRequires Rust 1.75 or later. Cross-compile targets are listed in the repository.
Quick start
# 1. Create a starter config in the current directory
blast init
# 2. Verify the config is valid and show all endpoints
blast validate
# 3. Hit every endpoint once to confirm they respond
blast check
# 4. Seed test data (runs endpoints tagged "seed")
blast seed --count 50 --concurrency 10
# 5. Fixed-rate load test (endpoints tagged "run")
blast run --rps 50 --duration 60
# 6. Stress ramp — finds the breaking point
blast stress --min-rps 10 --max-rps 200 --step 20 --step-duration 30Commands
blast init [path]
Creates blast.config.json in the given directory (default: current directory). The generated file includes example endpoints with fake data placeholders. Edit it to describe your API, then run blast check.
blast init
blast init ./my-apiblast validate
Loads and validates the config. Prints the base URL, total endpoint count, and a table of all endpoints with their method, path, and tags. Exits non-zero on any validation error — useful as a CI gate to catch broken configs early.
blast check
Hits every endpoint once in order. Merges global headers with per-endpoint headers. Extracts values from successful responses so later endpoints can use them. Prints a coloured pass/fail table with per-request timing. Exits non-zero on any failure.
blast seed
Runs all endpoints tagged "seed" N times with configurable concurrency. Each iteration is fully independent with its own extraction context. Use this to pre-populate a test database before a load test.
| Flag | Default | Description |
|---|---|---|
| --count | 10 | Total number of iterations |
| -j, --concurrency | 1 | Maximum parallel requests |
blast seed --count 1000 --concurrency 20blast run
Fixed-RPS load test. Uses a tokio interval ticker to maintain the target request rate. Round-robins over endpoints tagged "run". Prints live per-second progress and a final summary with p50, p95, p99, and p999 latency percentiles.
| Flag | Default | Description |
|---|---|---|
| --rps | 10 | Target requests per second |
| -d, --duration | 30 | Test duration in seconds |
| --ramp-up | 0 | Seconds to ramp from 0 to target RPS before measuring (0 = disabled) |
| --output | terminal | Output format: terminal, json, or html |
| --assert | — | Assertion like "p99<200ms" or "error-rate<1%" — exits non-zero on failure |
blast run --rps 100 --duration 120
blast run --rps 50 --ramp-up 30 --duration 60
blast run --output json
blast run --output html
blast run --rps 200 --assert "p99<300ms" --assert "error-rate<1%"blast stress
RPS ramp test. Steps from --min-rps to --max-rps in increments of --step. Stops early when p99 exceeds 500ms or the error rate exceeds 1%. Prints a per-step coloured result table and a final recommendation showing the last stable RPS.
| Flag | Default | Description |
|---|---|---|
| --min-rps | 10 | Starting RPS |
| --max-rps | 100 | Maximum RPS to reach |
| --step | 10 | RPS increase per step |
| --step-duration | 15 | Seconds to hold each step |
| --output | terminal | Output format: terminal or json |
| --assert | — | Assertion like "p99<500ms" — exits non-zero on failure |
blast stress --min-rps 10 --max-rps 500 --step 50 --step-duration 20
blast stress --output json
blast stress --assert "p99<500ms"blast mock
Starts a local HTTP server from your blast.config.json. Every endpoint becomes a live route. Frontend developers can point their app at http://localhost:<port> and build against realistic responses without waiting for the real backend.
Response bodies are read from the mock_response field on each endpoint. {{fake.*}} placeholders are resolved on every request, so each response gets fresh data. If no mock_response is defined, the route returns {"status": "ok"} with the declared status code.
| Flag | Default | Description |
|---|---|---|
| --port | 4000 | Port to listen on |
| --config | blast.config.json | Path to config (auto-detected if omitted) |
blast mock
blast mock --port 8080
blast mock --config ./api/blast.config.json$ blast mock --port 4000
Loaded blast.config.json
GET /api/v1/users 200
POST /api/v1/auth/register 201
POST /api/v1/auth/login 200
GET /api/v1/users/{id} 200
DELETE /api/v1/users/{id} 204
5 routes mounted
Listening on http://localhost:4000blast trace <name>
Runs a single named endpoint and prints the complete request/response round-trip: resolved URL, method, headers sent, request body, response status, response headers, response body, and latency. Setup endpoints run first so extracted values are available.
Useful for debugging request/response mismatches without leaving the tool — no curl flags to remember.
blast trace "login"
blast trace "get user"$ blast trace "login"
── request ─────────────────────────────────────
POST http://localhost:3000/api/v1/auth/login
Content-Type: application/json
{ "email": "admin@example.com", "password": "Admin1234!" }
── response (200 ✓ — 14ms) ──────────────────────
content-type: application/json
{ "data": { "access_token": "eyJ..." } }blast stage
Runs a multi-stage load profile defined in the stages array of blast.config.json. Each stage specifies a target RPS and duration. Set rps: 0 for a cooldown (sleep) stage. Per-stage stats are printed after each step.
blast stage{
"stages": [
{ "rps": 10, "duration": 30 },
{ "rps": 50, "duration": 60 },
{ "rps": 100, "duration": 120 },
{ "rps": 0, "duration": 30 }
]
}Configuration
blast reads blast.config.json from the current directory (or the path passed to --config). The file has a flat structure — a base URL, global headers, an optional setup array, and an endpoints array.
{
"base_url": "http://localhost:3000",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer {{token}}"
},
"setup": [
{
"name": "login",
"method": "POST",
"path": "/api/v1/auth/login",
"body": { "email": "admin@example.com", "password": "Admin1234!" },
"expect_status": 200,
"extract": { "token": "data.access_token" }
}
],
"endpoints": [
{
"name": "register user",
"method": "POST",
"path": "/api/v1/auth/register",
"body": {
"email": "{{fake.email}}",
"password": "{{fake.password}}",
"name": "{{fake.name}}"
},
"expect_status": 201,
"tags": ["seed"]
},
{
"name": "list users",
"method": "GET",
"path": "/api/v1/users",
"expect_status": 200,
"weight": 3,
"tags": ["run"]
},
{
"name": "get user",
"method": "GET",
"path": "/api/v1/users/{{user_id}}",
"expect_status": 200,
"tags": ["run"]
}
]
}Endpoint fields
| Field | Type | Description |
|---|---|---|
| name | string | Human-readable label shown in output |
| method | string | HTTP method — GET, POST, PUT, PATCH, DELETE |
| path | string | URL path, appended to base_url. Supports {{placeholders}}. |
| headers | object | Per-endpoint headers, merged with global headers |
| body | object | Request body (JSON). Supports {{fake.*}} placeholders. |
| expect_status | integer | Expected HTTP status code; counted as failure if response differs |
| extract | object | Map of variable name → dot-path to extract from response JSON |
| assert | object | Body assertions evaluated by blast check: { "data.count": ">0" } |
| weight | integer | Relative traffic weight for load distribution (default: 1) |
| scenario | string | Groups this endpoint into a named scenario sequence (e.g. "auth-flow") |
| tags | array | Commands that pick up this endpoint: "seed", "run", "stress" |
| mock_response | object | Body returned by blast mock for this route |
Stages
The top-level stages array defines a multi-stage load profile for blast stage. Each entry sets a target RPS and duration. A stage with rps: 0 is a cooldown — blast sleeps for the duration and fires no requests.
{
"base_url": "http://localhost:3000",
"stages": [
{ "rps": 10, "duration": 30 },
{ "rps": 50, "duration": 60 },
{ "rps": 100, "duration": 120 },
{ "rps": 0, "duration": 30 }
],
"endpoints": [...]
}| Field | Type | Description |
|---|---|---|
| rps | integer | Target requests per second for this stage (0 = cooldown/sleep) |
| duration | integer | How long to hold this stage in seconds |
Fake data
Use {{fake.*}} placeholders in request body examples or header values. A new value is generated per request — every iteration of blast seed and every request in blast run gets fresh data.
| Placeholder | Generates |
|---|---|
| {{fake.email}} | Random email address (e.g. john.doe@example.com) |
| {{fake.username}} | Random username |
| {{fake.password}} | 8–16 character password with mixed chars |
| {{fake.name}} | Full name |
| {{fake.firstname}} | First name only |
| {{fake.lastname}} | Last name only |
| {{fake.word}} | Single lorem word |
| {{fake.sentence}} | Lorem sentence (3–8 words) |
| {{fake.paragraph}} | Lorem paragraph (1–3 sentences) |
| {{fake.company}} | Company name |
| {{fake.city}} | City name |
| {{fake.country}} | Country name |
| {{fake.uuid}} | UUID v4 |
| {{env.VAR_NAME}} | Value of environment variable VAR_NAME |
Unknown placeholders produce a warning and are left unchanged in the output so they are easy to spot.
Setup phase
The top-level setup array in blast.config.json lists endpoints that run once in order before any load traffic. Extracted values from setup steps are shared with every subsequent request for the entire test.
If a setup step fails (wrong status code, network error), blast aborts immediately rather than firing load with a broken context. This prevents sending thousands of requests with a missing auth token.
The canonical use case is authentication: add a login endpoint to setup, extract the access token with extract, then include it in every load endpoint via the global headers using {{token}}.
Request chaining
The extract field on any endpoint stores response values in a shared context map keyed by variable name. Later endpoints reference them with {{name}} in any string field — headers, body values, or URL path parameters.
The dot-path walker descends into nested JSON objects (data.user.id) and array indices (items.0.id). Only scalar values (strings, numbers, booleans) are stored — objects and arrays emit a warning and are skipped.
{
"name": "login",
"method": "POST",
"path": "/api/v1/auth/login",
"body": { "email": "admin@example.com", "password": "Admin1234!" },
"expect_status": 200,
"extract": {
"token": "data.access_token",
"user_id": "data.user.id"
}
}{
"name": "get user",
"method": "GET",
"path": "/api/v1/users/{{user_id}}",
"headers": { "Authorization": "Bearer {{token}}" },
"expect_status": 200,
"tags": ["run"]
}Values extracted during setup are available to all subsequent endpoints. Values extracted during load operations are available to later endpoints in the same request round.
Scenarios
Adding scenario: "name" to endpoints groups them into ordered sequences. When scenarios are present in the config, blast run executes one full scenario sequence per iteration rather than round-robining individual endpoints. Each sequence runs with its own local extraction context, so values extracted in step 1 are available to step 2 — enabling realistic user journeys.
{ "name": "register", "method": "POST", "path": "/auth/register",
"scenario": "auth-flow", "expect_status": 201,
"extract": { "user_id": "data.id" } },
{ "name": "login", "method": "POST", "path": "/auth/login",
"scenario": "auth-flow", "expect_status": 200,
"extract": { "token": "data.access_token" } },
{ "name": "fetch profile", "method": "GET", "path": "/users/{{user_id}}",
"scenario": "auth-flow", "headers": { "Authorization": "Bearer {{token}}" },
"expect_status": 200 }Variable files
Pass --vars path/to/vars.json to any command to inject a flat JSON object of variables into the template context. These have the lowest precedence — they are overridden by values extracted from responses or set by environment variables.
{
"base_user": "admin@example.com",
"org_id": "acme-corp",
"tier": "enterprise"
}blast run --vars staging.json
blast check --vars ./env/prod.jsonOnly scalar values (strings, numbers, booleans) are accepted. Nested objects and arrays produce a warning and are skipped.
Threshold assertions
Pass one or more --assert flags to blast run or blast stress to enforce performance thresholds. blast evaluates assertions after the test and exits non-zero if any fail — enabling performance gates in CI pipelines.
| Metric | Example | Description |
|---|---|---|
| p50 | p50<100ms | Median latency |
| p95 | p95<200ms | 95th percentile latency |
| p99 | p99<500ms | 99th percentile latency |
| p999 | p999<1000ms | 99.9th percentile latency |
| error-rate | error-rate<1% | Percentage of failed requests |
| success-rate | success-rate>99% | Percentage of successful requests |
blast run --rps 100 --duration 60 \
--assert "p99<200ms" \
--assert "error-rate<1%"
# exits 0 if all pass, non-zero with details if any failBody assertions (per-endpoint) use the assert field and are evaluated by blast check. See Endpoint fields.
History
After every blast run or blast stress, blast automatically saves the result to ~/.blast/history/. On the next run against the same config, it prints a one-line comparison showing how p50, p95, p99, and p999 changed.
$ blast run --rps 100 --duration 60
...
vs last run:
p50 42ms → 38ms (-9%) ▼
p95 89ms → 91ms (+2%) →
p99 142ms → 198ms (+39%) ▲No flags needed — history runs silently and never affects the exit code. Records are keyed by the canonical config file path.