A desktop-first API client built with Kotlin and Compose Multiplatform.
No accounts. No telemetry. No cloud lock-in. Just fast, scriptable HTTP.
ReqLab is a Kotlin + Compose Multiplatform API client for desktop (macOS, Linux, Windows) and web. It is designed for fast request iteration, scriptable validations, and reproducible API test workflows — running entirely offline with no accounts required.
Requirements: JDK 17+ (JDK 21 recommended).
git clone https://github.com/snj07/req-lab.git
cd req-lab
./gradlew :ui-desktop:runNote for macOS users: If you see a warning that the app "cannot be opened because Apple cannot check it for malicious software":
- Right-click the app -> Open -> Open (bypasses the warning once), or
- Go to System Settings -> Privacy & Security -> "Open Anyway" (after attempting to open the app).
You can import these sample fixtures from the repo:
- Collection sample: qa-tests/fixtures/reqlab-test-collection.json
- Environment sample: qa-tests/fixtures/reqlab-test-environment.json
The collection includes an LLM (OpenAI-compatible) folder, an SSE folder, and an MCP (Model Context Protocol) folder. Start the sample server, then send LLM Chat Completions Stream (visible) or an SSE item to watch events arrive, or open an MCP item and Connect. See docs/mcp.md.
- Methods:
GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD - URL editing with live query-parameter table synchronisation
- Request headers editor (key/value table)
- Body types: JSON (JSON5 authoring by default; Send converts to strict JSON), GraphQL, form-data, x-www-form-urlencoded, raw text, binary
- Auth: None, Basic, Bearer Token, API Key, JWT
- Retry controls and per-request timeout
- HTTP streaming: SSE (
text/event-stream) and NDJSON on a single request (OpenAI-style"stream": true). Collection items withAccept: text/event-streamshow an SSE badge in the HTTP method color; folder ⋮ → New SSE Request - Copy request as
curl
ReqLab is an MCP client in the same workspace as REST: collections, environments, auth, and a shared Response pane.
- Transports: Streamable HTTP, Auto (legacy fallback), Legacy HTTP+SSE, desktop stdio
- Tools (Form/JSON), resources (read + subscribe), prompts — results in the shared Response pane
- Activity JSON-RPC inspector; Logs vs Console
- Sampling, roots, elicitation on the Client tab
- Same auth editors as REST (None / Basic / Bearer / API Key / JWT);
{{variables}}in URL, command, headers, and auth - Collection save, import, and export of MCP items
Full guide: docs/mcp.md
- Status code, status text, and response headers
- Response body with syntax-highlighted viewer (JSON, XML, HTML, GraphQL, JS)
- Live assembled stream text while a request is in flight; Body shows the assembled answer, RAW shows each SSE/NDJSON event
- Response timing (total, TTFB, time to first/last token, DNS, TCP, TLS, body) and payload size
- Structured pass/fail test result reporting from post-request scripts
A full-featured Compose-native code editor — no WebView, no Electron.
- Syntax highlighting: JSON, XML/HTML, GraphQL, JavaScript
- Code folding: brace-based (
{ },[ ]), tag-based, comment-based; Fold All / Unfold All - In-editor search: incremental match count, Previous/Next, keyboard-accessible
- Auto-format: JSON pretty-print, XML/HTML indentation, JavaScript formatting (script editor + body editor)
- Line numbers gutter, word wrap toggle, monospace font
- Virtualized rendering (no jank on responses over 10 MB)
- Full clipboard support (copy, cut, paste, select all) in both edit and read-only modes
JavaScript pre-request and post-request scripts through the reqlab namespace.
- Pre-request: mutate URL, headers, body, query params, and auth before dispatch
- Post-request: assert status, headers, timing, body, assembled stream text, and LLM fields (
finish_reason, token usage); persist extracted values - Console logging via
reqlab.console.log(...) - HTTP sub-requests via
reqlab.sendRequest()— make real HTTP calls from within scripts (fetch tokens, look up resources, chain requests)
// Pre-request: fetch a token and inject it as a header
reqlab.sendRequest({
url: "https://api.example.com/auth/token",
method: "POST",
header: [{ key: "Content-Type", value: "application/json" }],
body: { mode: "raw", raw: JSON.stringify({ username: "alice", password: "secret" }) }
}, function(err, resp) {
reqlab.request.headers.upsert("Authorization", "Bearer " + resp.json().token)
})See the full guide: docs/scripts.md
Four scopes, all interpolated as {{variable}} in URL, headers, body, and auth:
| Scope | API | Lifetime |
|---|---|---|
| Environment | reqlab.environment.* |
Persisted with selected environment |
| Global | reqlab.globals.* |
Persisted workspace-wide |
| Collection | reqlab.collectionVariables.* |
Session-scoped runtime map |
| Request | reqlab.variables.* |
Read-only merged view (request → collection → env → global) |
Named test blocks with a rich assertion chain:
reqlab.test("status is 200", () => {
reqlab.expect(reqlab.response.status).to.equal(200);
});Assertions: equal, eql, include, match, above, below, at.least, at.most, lengthOf, property, oneOf, ok, true, false, null, not.*, and more.
- Unlimited collections with nested folders
- Multi-tab request workflow with tab reorder and persistence
- Request history with badge counts and sidebar sync
- Export and import as JSON (ReqLab-native or Postman format)
⚠️ Experimental feature — Postman collection and environment import, including automaticpm.*/postman.*script conversion, is experimental. While most common patterns are supported, complex or unsupported Postman APIs may not convert correctly and may require manual adjustments after import.
Import Postman Collection v2 / v2.1 and Postman Environment files directly.
- All folders, requests, URLs, methods, headers, bodies, and auth types
pm.*script calls are automatically rewritten toreqlab.*equivalents- Legacy
postman.*API calls (pre-v6) are also converted on import - Disabled headers and variables are skipped on import
pm.sendRequestis fully supported — translated toreqlab.sendRequest()automaticallypm.execution.setNextRequest,pm.execution.skipRequest, andpostman.setNextRequestare translated toreqlab.execution.*- See docs/scripts.md for the full conversion reference
| Shortcut | Action |
|---|---|
⌘ + Enter / Ctrl + Enter |
Send request (or cancel in-flight request) |
⌘ + Shift + [ / Ctrl + Shift + [ |
Move active tab left |
⌘ + Shift + ] / Ctrl + Shift + ] |
Move active tab right |
⌘ + S / Ctrl + S |
Save active request |
⌘ + W / Ctrl + W |
Close active tab |
⌘ + N / Ctrl + N |
New request tab |
⌘ + , / Ctrl + , |
Open Settings |
Full shortcut reference (including editor shortcuts): docs/shortcuts.md
Open from the toolbar Help icon or via Settings → Open Help & About:
- Feature overview and usage flow
- Keyboard shortcuts reference
- Scripting overview
- Version and build info
| Document | Description |
|---|---|
| CHANGELOG.md | Release notes |
| DEVELOPMENT.md | Build, run, and contribute locally |
| docs/architecture.md | Module structure and data flow |
| docs/editor-architecture.md | Code editor internals |
| docs/mcp.md | MCP client: tools, resources, prompts, Activity, sampling |
| docs/scripts.md | Scripting API and variable scopes |
| docs/shortcuts.md | Keyboard shortcut reference |
| docs/testing.md | Test strategy and coverage matrix |
| docs/tests.md | Test run commands |
Contributions are welcome. Please use GitHub Flow: create a branch, add commits, and open a pull request.
For larger changes, open an issue first to align on approach and scope.
ReqLab uses GitHub Actions for CI. Push to main runs the quality gate; tags matching v* run the full build and publish a GitHub Release.
See the workflow: .github/workflows/release.yml
Apache 2.0 — see LICENSE.



