Reqly
A modern, lightweight HTTP client that lives inside VS Code. Compose a request, send it, and read the response without ever leaving the editor.
Why Reqly for VS Code exists
Reqly is an open source HTTP client for developers. The hosted
web app is excellent for public APIs, but a browser can only reach what the browser is allowed to
reach: localhost services, self-signed certificates, and reserved headers are all off limits.
This extension removes that ceiling. Requests run in the VS Code extension host over Node's own HTTP
stack, so there is no browser sandbox in the way. The API you are building in one tab is one keystroke
away in the next, and nothing about your request ever leaves your machine.
Features
Collections you can read, diff and commit
A collection is one JSON file. Renaming it renames the file; deleting it deletes the file. Saving a
request rewrites that collection's file and nothing else, so a library of hundreds of requests stays
quick to open and quiet in a diff.
Point reqly.storage.location at workspace and the files land in a .reqly folder in the project,
where they can be reviewed in a pull request and travel with the repository. Edit one by hand, pull a
change, switch branches: the sidebar follows the files. Credentials never go in them — those live in
the operating system keychain, so a collection file is safe to commit.
Collections that keep their shape
The Reqly panel in the Activity Bar holds your whole workspace: collections at the top level, folders
nested inside them, and requests anywhere, including loose at the root when a collection would be
overkill. Create, rename, duplicate, delete and reorder from a right-click menu or by dragging rows
around. Renaming happens inline, on a double click or F2, the way the file explorer works.
Everything is stored globally rather than per project, so your collections are there in every window
and survive closing VS Code, updating the extension, and restarting the machine.
The structure is normalized internally and every operation addresses a single node by its own id,
never by a parent-plus-child pair. Moving a request while it is open cannot make a later save land on
the wrong row, because nothing anywhere caches which collection a request belongs to. On load the
stored tree is validated and, if it is ever inconsistent, repaired deterministically and reported
rather than silently reshaped.
Environments that follow the project, not the machine
Write {{baseUrl}} once and point it at development, staging or production. Values come from three
places, and the first one that answers wins:
| Scope |
Where it lives |
Good for |
| Dynamic |
Built in |
{{$guid}}, {{$timestamp}}, {{$isoTimestamp}}, {{$randomInt}}, {{$randomAlphaNumeric}} |
| Environment |
One file per environment |
The URL, token or tenant that changes per machine |
| Collection |
Inside the collection file |
Defaults that should travel with the collection |
An environment overrides the collection, so a collection can ship a sensible default that a machine
replaces. Define a variable named after a dynamic one and yours wins, which is how you pin a
timestamp while debugging.
Type {{ in the URL, in any key or value, in the request body or in the authentication fields and
the names in scope are offered, filtered as you type. A name that resolves is green; a name nothing
defines is amber and underlined, so a typo shows before the request goes out rather than after.
Mark a value with the lock and it is kept in the operating system keychain instead of the file.
Collection variables cannot be marked secret on purpose: that file is meant to be shared, so
credentials belong in an environment.
A request panel wired to your collections
Clicking a request in the sidebar opens it in the panel and links the two. The header shows its name,
the collection and folder it lives in, and a dot when there are unsaved edits. Save or Ctrl+S
writes the changes back; an unlinked request offers Save to… and asks where it should live.
Renaming, moving or deleting a request from the sidebar while it is open is handled live: the header
follows the rename, and a delete simply unlinks the panel instead of leaving it pointing at something
that no longer exists.
A full request builder
Pick any of the seven HTTP methods (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS), type
a URL, and go. Query parameters and headers live in their own tabs, each row with an enable toggle so
you can park a value without deleting it. Tabs show a live count of everything currently active, so
you always know what is going out on the wire.
Type a bare host such as api.example.com/health and Reqly fills in https:// for you.
Every body format you actually use
JSON, XML and plain text get a real editor: line numbers, syntax highlighting, auto-closing brackets
and quotes, smart indentation on Enter, and a Beautify button that reformats JSON in place. URL
encoded forms and multipart form data get a row editor, and multipart fields can carry real file
attachments picked through the native VS Code dialog. You can also send any file from disk as a raw
binary body.
Bodies are skipped automatically on GET and HEAD, and the right Content-Type is inferred unless
you set one yourself.
Authentication without boilerplate
Bearer tokens (with a customizable scheme prefix), Basic auth, and API keys that go either into a
header or into the query string. Reqly encodes and attaches the credential at send time, so it never
clutters your headers tab.
A response inspector that answers questions
Status code, duration and payload size sit above a syntax-highlighted body with pretty-print and line
wrapping toggles. Images render as an actual preview. Binary and oversized responses are recognized
instead of dumped as noise, and both can be written straight to disk with Save. Full response
headers get their own tab, with a live count.
A timing waterfall
The Timeline tab breaks the request down into DNS lookup, TCP connect, TLS handshake, time to
first byte, and download, each with its own bar. Below it you get the negotiated HTTP protocol, the
final URL, and every hop of the redirect chain that led there. Timings are measured per hop and added
up, so the numbers stay honest even when a request is redirected several times.
cURL in and out
Paste a curl command anywhere in the URL field and Reqly imports it: method, URL, query string,
headers, body, and -F form fields, including @path file attachments. It recognizes the common flag
spellings (-X/--request, -H/--header, -d/--data-raw/--data-binary, -F/--form,
-u/--user, --url) and picks the request tab most worth showing you afterwards.
Going the other way, Copy as cURL in the send menu turns whatever is on screen into a runnable
command, ready to paste into a terminal, a ticket, or a teammate's chat.
Per-request settings
A gear next to the send button controls the request timeout, whether 3xx responses are followed
automatically, whether TLS certificates are verified, and how much of a response is read into memory.
The timeout is a deadline for the whole request, shared across the redirect chain, not just a guard
against an idle socket.
TLS verification trusts the certificate authorities installed on your machine, not only the ones
compiled into Node, so a corporate root or an inspection proxy works without turning verification
off. For an authority that is not in the system store, point reqly.certificateAuthority at its PEM
file. Turning verification off is still there for a throwaway self-signed certificate.
Redirects are handled carefully: Authorization and Cookie headers are dropped when a hop crosses
origins, and the request body and its Content-Type are dropped when the status code says they
should be.
Built for real payloads
Responses are decompressed automatically for gzip, deflate and brotli, and text is decoded
using the charset the server declared rather than a guess. A response is read up to the configured
size limit and reported as cut short past it, measured after decompression, so an endpoint returning
far more than you expected cannot exhaust the editor's memory. Long-running requests can be cancelled
mid-flight with the same button that sent them.
At home in your editor
Every color comes from the Reqly design system and
follows your editor between light and dark themes, live, with no configuration. Nothing is lost on a
reload: collections come back from their files, and the request you were editing, saved or not, comes
back exactly as you left it in that workspace.
Getting started
- Click the Reqly duck in the Activity Bar.
- Hit New Request to start a loose request, or New Collection to group a few together.
- Name it inline, pick a method, type a URL, and press
Enter.
- Press
Ctrl+S to save your changes back to the request.
- Need the same request against more than one server? Open the environment picker in the panel
header, add an environment, and swap
https://localhost:5208 for {{baseUrl}}.
Commands
| Command |
Description |
Reqly: New Request |
Creates a request, opens it in the panel, and starts renaming it. |
Reqly: New Collection |
Creates a collection and starts renaming it. |
Reqly: Open HTTP Client |
Opens the request panel in the editor area. |
Reqly: Send Request |
Sends whatever the panel currently holds. |
Reqly: Save Request |
Writes the panel back to its saved request, asking where if unlinked. |
Reqly: Manage Environments |
Opens the variables editor for the collection and every environment. |
Reqly: Select Environment |
Picks the environment used to resolve {{variables}}. |
Keyboard shortcuts
In the request panel:
| Shortcut |
Action |
Enter in the URL field |
Send the request |
Ctrl+Enter / Cmd+Enter |
Send from anywhere in the panel |
Ctrl+S / Cmd+S |
Save the request |
Ctrl+Alt+Enter / Cmd+Alt+Enter |
Send from anywhere in VS Code |
In the collections sidebar:
| Shortcut |
Action |
↑ / ↓ |
Move the selection |
Enter |
Open a request, or toggle a group |
F2 |
Rename the selected item |
Delete |
Delete the selected item |
| Double click |
Rename inline |
| Right click |
Open the context menu |
Built with
Architecture
The extension ships three bundles that never import each other, only the pure modules between them:
src/
├── core/ → pure logic shared by every bundle
│ (types, the collection tree, curl, formatting, message contracts)
├── http/ → the transport: build a request, execute it, decode the response (Node only)
├── panel/ → the request panel controller and its HTML shell
├── providers/ → the collections sidebar controller and its HTML shell
├── services/ → collection, environment and secret storage
├── utils/ → small shared helpers
├── collections/ → the UI that runs inside the sidebar (DOM only, no VS Code API)
└── webview/ → the UI that runs inside the panel (DOM only, no VS Code API)
└── components/
src/extension.ts runs in the Node extension host and owns the network, the file system, the
clipboard and the collection tree. src/webview/main.ts and src/collections/main.ts run inside
their webviews and own only the DOM. They never share objects, only the typed messages declared in
src/core/messages.ts and src/core/collectionsMessages.ts, which keeps the boundary explicit and
every side independently testable. Everything under src/core is free of both vscode and
document, which is exactly why the test suite can exercise it directly.
Variables follow the same split. src/core/variables.ts owns the token grammar, the scope merge and
the interpolation, with no knowledge of VS Code or the DOM, which is why both the host (resolving a
request before it is sent) and the webview (painting a token, offering a completion) can share one
implementation instead of drifting apart. src/core/environmentFile.ts owns the on-disk shape, so a
future importer reuses the same parser the file system already uses.
The collection tree deserves its own note. It lives in src/core/workspace.ts as a normalized map of
nodes plus ordered childIds, with no stored parent pointers: a node's parent is always derived, so
two sources of truth cannot disagree. Every mutation is a pure function taking a single node id and
returning either a new tree or a reason it refused, which means an operation can never quietly land
on the wrong row or silently do nothing. WorkspaceService is the only writer, and the sidebar
webview receives a flat list of rows to draw rather than the tree itself, so it makes no structural
decisions at all.
Requirements
- Visual Studio Code
^1.134.0.
- Network access to whatever you are calling. Nothing else: Reqly has no account, no telemetry, and
no backend of its own.
Extension Settings
Request options — timeout, redirect following, TLS verification, maximum response size — live with
the request itself, behind the gear icon next to the send button, so two workspaces can disagree
without fighting over a global setting.
Two settings live in settings.json, because they describe the machine and the project rather than
the request:
| Setting |
Type |
Default |
Description |
reqly.storage.location |
string |
"global" |
Where collection files are kept: global for Reqly's own storage, workspace for a .reqly folder in the project. |
reqly.certificateAuthority |
string[] |
[] |
Paths to extra PEM certificate authorities to trust, for a root that is not installed in the system store. |
Changing reqly.storage.location needs a window reload, and existing files are not moved for you.
With no folder open, workspace falls back to global storage.
Requests go out through the editor's own HTTP stack, so VS Code's http.proxy setting and the
standard proxy environment variables apply, following whatever http.proxySupport is set to.
Credentials are not kept in settings.json or in extension state. Bearer tokens, Basic auth
passwords, API key values and any variable marked secret go to VS Code's SecretStorage, backed by
the operating system keychain.
Known Issues
- One request open at a time: opening another replaces what the panel is showing, and sending again
replaces the request in flight.
- Deleting is permanent. There is a confirmation prompt, but no undo afterwards.
- Responses over 5 MB are not previewed in the panel. Use Save to write them to disk.
- Responses are held in memory while they are read, so the size limit is a real ceiling rather than a
display threshold. There is no streaming to disk yet.
- No cookie jar: a
Set-Cookie on one response is not sent back on the next request.
- A variable cannot be filled from a previous response yet: that needs a scripting step, which does
not exist.
- No client certificate (mTLS) authentication.
- No request history yet.
Roadmap
- Import and export, including Postman and Insomnia collections.
- Chaining: writing a value from a response into a variable.
- Multiple request tabs.
- Request history.
.http and OpenAPI file import.
- A cookie jar for session based APIs.
- Client certificates for mTLS.
Contributing
Contributions are welcome. To run the extension locally:
npm install
npm run watch
Then press F5 in VS Code to launch an Extension Development Host with Reqly loaded.
Useful scripts:
npm run watch: rebuild both bundles on every change.
npm run check-types: run TypeScript without emitting.
npm run lint: run ESLint over src.
npm run format: format the project with Prettier.
npm run compile-tests: build the unit tests into out/.
npm test: run the unit test suite.
npm run package: produce the production bundles.
Following extension guidelines
This extension follows the official Visual Studio Code extension guidelines.
Author
Built by Artur Bomtempo.
License
Released under the MIT License.
Enjoy!