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
- 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, GraphQL, form-data, x-www-form-urlencoded, raw text, binary
- Auth: None, Basic, Bearer Token, API Key, JWT
- Retry controls and per-request timeout
- Copy request as
curl
- Status code, status text, and response headers
- Response body with syntax-highlighted viewer (JSON, XML, HTML, GraphQL, JS)
- Response timing (total, TTFB, 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, and body; 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 |
|---|---|
| DEVELOPMENT.md | Build, run, and contribute locally |
| docs/architecture.md | Module structure and data flow |
| docs/editor-architecture.md | Code editor internals |
| 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.


