Build a chained API test
This tutorial builds a multi-block runbook that simulates a real end-to-end API check: authenticate, use the token, verify the result was what you expected. It’s the workflow you’ll reach for every time you debug staging or write a smoke test for CI.
Time: ~15 minutes · Prereqs: finished Quickstart.
What you’ll build
Section titled “What you’ll build”By the end you’ll have a runbook with 3 chained blocks:
graph LR A[login] -- token --> B[me] B -- user.id --> C[assert]login→ POST/auth/login, captures the JWTme→ GET/users/mewith the bearer token fromloginassert→ an# expect:section that fails the runbook iftimeorstatusdrift
httui handles the dependency graph automatically — running me runs
login first; running assert runs both.
1. The API you’ll hit
Section titled “1. The API you’ll hit”We’ll use httpbin and a tiny fake-auth helper — both public, no signup. The “login” returns whatever you send as a fake “token”, and “/anything” echoes your request so you can confirm the bearer header reached the server.
For the assertion we’ll target a fixed-shape /json endpoint so we can
assert on real fields.
2. Create the runbook
Section titled “2. Create the runbook”In runbooks/, create auth-flow.md. Open it and add a short intro at
the top:
# Auth flow smoke test
A two-step login → me → assert chain to verify the staging gatewayisn't dropping bearer tokens.3. Block 1 — login
Section titled “3. Block 1 — login”Below the intro, paste this HTTP block. The alias=login on the
fence line names the block so we can reference it later:
```http alias=loginPOST https://httpbin.org/postContent-Type: application/json
{ "user": "alice", "device": "laptop-42"}```Hit Cmd+Enter. You should see the httpbin echo response with your
JSON in the json field.
4. Block 2 — me (uses the token)
Section titled “4. Block 2 — me (uses the token)”Now the chain. Add below:
```http alias=meGET https://httpbin.org/anythingAuthorization: Bearer {{login.body.json.user}}-{{login.body.json.device}}Accept: application/json```The reference {{login.body.json.user}}-{{login.body.json.device}}
reads alice and laptop-42 out of the login response and uses
them as a fake bearer token. (In production you’d reference an actual
JWT field like {{login.body.access_token}}.)
- Click anywhere in the
meblock body. - Press
Cmd+Enter. - httui sees
medepends onlogin→ runsloginfirst (cached from step 3 — instant). - Resolves the references, sends
me, shows the response. - In the response panel, expand the
headersfield — confirmAuthorization: Bearer alice-laptop-42reached httpbin.
You just chained two real API calls together inside one markdown file.
5. Block 3 — assert
Section titled “5. Block 3 — assert”Add a third block, but this time use the fixed-shape /json endpoint
and an # expect: section:
```http alias=assertGET https://httpbin.org/json
# expect:# status == 200# time < 1500ms# body.slideshow.title contains "Sample"```The # expect: section turns the block from “show me the response”
into “fail the runbook if any of these aren’t true”.
Hit Cmd+Enter on assert. You should see all three assertions
pass (green check next to each line).
6. Run the whole runbook
Section titled “6. Run the whole runbook”Press Cmd+Shift+Enter (or click the Run all button in the
status bar). httui:
- Builds the dependency DAG (
login→me,assertstandalone). - Runs them in topological order, in parallel where possible.
- Shows pass/fail counts in the status bar.
For our 3 blocks it takes ~600ms total. In a real runbook with 20 blocks against staging, you’d see the parallel runs fan out visibly in the panel.
What’s next
Section titled “What’s next”You now have the loop down: block → capture → reference → assert. That’s 80% of what httui is for. From here:
- Add a database to your runbook — same chain idea but with SQL: fetch a row, use the id in an HTTP request, assert the response.
- Block references — the full
{{...}}syntax, scoping rules, and how{{$prev}}works. - Environments —
{{BASE_URL}}and similar: per-env variables so the same runbook hits staging, prod, or your laptop tunnel.