Why I Stopped Shipping Postman Collections and Started Committing .http Files
IntelliJ IDEA Ultimate's built-in HTTP Client turns REST API requests into plain-text, version-controlled .http files. A worked login-and-token example, honest trade-offs versus Postman, and how to verify support in your IDE.
29 Jan 2026, 10:42 UTC

Every team I have seen test REST APIs by hand ends up in the same place: a Postman collection that lives on one person's machine, drifts out of sync with the code, and gets shared as a JSON export nobody reviews. IntelliJ IDEA Ultimate's built-in HTTP Client offers a different trade: your API requests become plain-text files that sit next to the code they exercise, diff cleanly in pull requests, and run from the editor gutter. It is not a Postman replacement for everyone — but for developers, it is often the better default.
What the HTTP Client actually is
The HTTP Client is a feature of IntelliJ IDEA Ultimate (it is not included in the free Community edition — more on that below). You create a file with an .http or .rest extension anywhere in your project, write requests in a simple text format, and click the green gutter icon next to a request to run it. Responses open in the Services/Run tool window, and the IDE keeps a history of recent responses per request.
Multiple requests live in one file, separated by ### lines. Because the file is just text, it commits to Git like any other source, and changes to an endpoint can be reviewed in the same pull request as the code that implements it.
A worked example: login, then an authenticated call
Here is a realistic two-request file for a typical token-based API. Save it as requests/orders.http in your project:
### Log in and capture the token
POST {{host}}/auth/login
Content-Type: application/json
{
"username": "demo",
"password": "demo-password"
}
> {%
client.test("login succeeds", function() {
client.assert(response.status === 200, "expected 200");
});
client.global.set("token", response.body.accessToken);
%}
### Fetch orders using the captured token
GET {{host}}/api/orders
Authorization: Bearer {{token}}
Two things are doing the work here. First, {{host}} is a variable, resolved from an environment file so the same request file can target local, staging, or production without edits. Second, the > {% ... %} block is a small JavaScript response handler: it asserts the status code and stores the returned token in a global variable, which the second request then sends as a Bearer header. Run the login request first, then the orders request, and the token chains through automatically.
The environment file, http-client.env.json, sits next to your requests:
{
"local": {
"host": "http://localhost:8080"
},
"staging": {
"host": "https://staging.example.com"
}
}
You pick the active environment from a dropdown in the editor before running. For secrets, the convention is a separate http-client.private.env.json file — add it to .gitignore, because it is the natural place for real credentials and it is easy to commit by accident.
Where this beats a GUI tool — and where it doesn't
The wins are structural rather than flashy:
- Reviewability. A new endpoint and the requests that exercise it land in the same diff. Reviewers can actually read them.
- No sync, no account. There is no cloud workspace to join, no export/import dance, no "which version of the collection is current?"
- Always in reach. When you are mid-debugging in the IDE, running a request is a gutter click, not a context switch to another app.
The trade-offs are real, though:
- Ultimate only. Teammates on the Community edition — or on VS Code, or on no IDE at all — cannot run these files directly. If you standardize on
.httpfiles as your only API test artifact, part of the team is locked out. - Not for non-developers. QA or support staff who are comfortable in Postman's UI will not enjoy editing raw text and JavaScript handler blocks.
- No load testing, no mock servers, no monitors. The JavaScript handler API is deliberately minimal. Anything beyond smoke-level assertions belongs in a real automated test suite (JUnit, REST Assured, your framework of choice), not in a
.httpfile.
The honest framing: .http files are an excellent complement to automated API tests and a good replacement for ad-hoc GUI poking. They are not a CI strategy.
Verify it in your own IDE before committing to it
Syntax details and the response-handler API have evolved across releases, so check your installed build rather than trusting any article (including this one):
- Create a scratch file
scratch.http, write a singleGET https://httpbin.org/get, and look for the run icon in the gutter. No icon usually means you are on Community edition or the feature is unavailable in your version. - Open the Help menu and search for the HTTP Client documentation, or invoke the "Explore HTTP Client" action, to see the exact syntax your build supports.
- Add an
http-client.env.jsonwith ahostvariable and confirm a{{host}}reference resolves — hover or run to check substitution. - Add a
client.testblock asserting a 200 status and confirm the assertion result shows in the run output.
If all four work, you have everything you need for the login-and-chain pattern above.
The takeaway
If your team is on IntelliJ IDEA Ultimate and your API testing currently lives in someone's unshared Postman workspace, move the requests into .http files and commit them. You get versioned, reviewable, environment-switchable API checks for free. Keep the secrets in a gitignored private env file, keep the heavy assertions in your real test suite, and confirm the syntax against your IDE version before you roll it out as a team standard.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.