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 | sh

Detects 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 | iex

Installs 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/blast

Requires 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 30

Commands

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-api

blast 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.

FlagDefaultDescription
--count10Total number of iterations
-j, --concurrency1Maximum parallel requests
blast seed --count 1000 --concurrency 20

blast 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. Prints a final summary with p50, p95, p99, and p999 latency percentiles.

FlagDefaultDescription
--rps10Target requests per second
-d, --duration30Test duration in seconds
blast run --rps 100 --duration 120

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.

FlagDefaultDescription
--min-rps10Starting RPS
--max-rps100Maximum RPS to reach
--step10RPS increase per step
--step-duration15Seconds to hold each step
blast stress --min-rps 10 --max-rps 500 --step 50 --step-duration 20

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.

FlagDefaultDescription
--port4000Port to listen on
--configblast.config.jsonPath 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:4000

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

FieldTypeDescription
namestringHuman-readable label shown in output
methodstringHTTP method — GET, POST, PUT, PATCH, DELETE
pathstringURL path, appended to base_url. Supports {{placeholders}}.
headersobjectPer-endpoint headers, merged with global headers
bodyobjectRequest body (JSON). Supports {{fake.*}} placeholders.
expect_statusintegerExpected HTTP status code; counted as failure if response differs
extractobjectMap of variable name → dot-path to extract from response JSON
weightintegerRelative traffic weight for load distribution (default: 1)
tagsarrayCommands that pick up this endpoint: "seed", "run", "stress"
mock_responseobjectBody returned by blast mock for this route

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.

PlaceholderGenerates
{{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.

Tags

The tags array on an endpoint controls which blast command picks it up. An endpoint can appear in multiple commands by listing multiple tags.

TagCommandNotes
"seed"blast seedRuns N times; each iteration independent
"run"blast runRound-robined for the test duration
"stress"blast stressRound-robined per step

If no endpoints have any tags, all commands fall back to using every endpoint. Endpoints in the setup array are always excluded from tag matching — they run once before load regardless of tags.

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.