A better curl, powered by collections.
Your requests are Markdown files in your repo. rq runs them, renders the
response as something you can read, and converts whatever you have today — telling you
plainly about anything it could not carry.
That last part is a template in the file, not a feature request. A request document can render its own response, so the thing you look at fifty times a day is the thing you shaped.
---
url: '{{api}}/repos/{{owner}}/{{repo}}/issues'
query:
state: open
vars:
GH_TOKEN: { env: GH_TOKEN, secret: true }
capture:
first: response[0].number
---
-- view --
{% for i in response %}
- [**#{{ i.number }}**]({{ i.html_url }}) {{ i.title }}
{% endfor %}
Getting started
You do not have to convert anything to try it. Point rq at what you already have:
The collection stays exactly as it is — this is the same converter rq import
runs, in memory. When you want it as an rq project, import it.
What it does
Chains without JavaScript
A request declares what it depends on and what it takes from the response. The common case — log in, use the token — needs no code at all.
---
url: '{{api}}/me'
parents: [login]
headers:
Authorization: 'Bearer {{token}}'
---
Scripts, in whatever dialect they were written in
Postman's pm.* and Bruno's bru.* run unmodified. The file keeps the
script its author wrote; the runtime reconciles it at execution, every run. That covers
Postman v2.1, v2.0, and the v1.0 collections still sitting in repositories —
tests['ok'] = responseCode.code === 200 and all.
A network panel for the terminal
rq r issues -c opens the run you just did: arrow between steps, drill into the
request, the response, every header, and where the milliseconds went. Nothing is re-sent —
a panel that re-issued your POST to show you what it did would be a bug. With
--log it reaches back past this process to everything you have sent.
Files people edit, so a checker
All three are errors, and the last one is the policy in miniature: a request still carrying
{{TOKEN}} is not sent. Putting a placeholder on the wire is
always a bug — Bearer {{TOKEN}} comes back 401 and reads like a credentials
problem, so you go looking at the API instead of at your file, and the request has already
gone out. Failing costs one command. Declaring a variable is how you say it may
legitimately be empty.
Formats
Everything maps through one idealised model, so a new format is one importer or one exporter, not glue against every other format.
| Format | Import | Export |
|---|---|---|
| Postman v2.1 / v2.0 / v1.0 | yes | v2.1 |
Bruno .bru v2 | yes | yes |
| rq (Markdown project) | yes | yes |
| cURL | yes | — |
Requestly (LOCAL_FS, MappedItems) | — | yes |
OpenAPI, Insomnia, HAR, .http, Hurl | planned | |
Conversion is checked against collections other tools produced — usebruno's own 223-request test collection, Adyen's published Postman collections, newman's samples — and each gate asserts more than "it parsed": every request in the source has to survive into the model, and round-trip loss has to stay inside a documented list. A conversion that drops something says so.
Where this is
Version 0.1: the loop works end to end, and the packaging does not exist yet. There is a longer account of the thinking and the current state in the first post.