TL;DR : I've created the first version of an openapi-generator for the jetbrains HTTP Client, and together with the CLI runner it allows you to play against APIs without ever going out of your terminal and it can even run in your CI/CD pipeline. See repository here
I work a lot with APIs, whether for an app I develop myself, or to interact with others. Most people I know tend to use Postman for this. But I don't quite like the product any more, and it forces me to move out of my IntelliJ environment which really kills my productivity.
With a combination of the Jetbrains HTTP Client and OpenAPI generators we can do much better, in a semi automated way and even reuse our code on our CI/CD. Let's dive into how!
A quick introduction to the Jetbrains HTTP Client
A lot of people don't know about the Jetbrains HTTP Client, which is a Jetbrains proprietary text file format that allows you to run API requests easily.
One thing I love about it is that it's integrated into the IDE, and it can generate those requests for you. For example, looking at this Spring Boot Kotlin Sample, next to each Mapping
there is a "Open in HTTP Client" option in the gutter.
The "Open in HTTP Client" option in the gutter of our IDE
When clicking it, it will generate a scratch file for this request :
The generated request
You can do many things with those requests, like setting Content-Type, sending body payload and more but I'll direct you to the documentation for more.
The one thing that I want to show you here is that it defines variables for you that you can fill in using a separate configuration file. Pick an environment, and fill in a value for the variables you need. You can either create a public file, or a private one for, say, API Keys.
Me creating a Public Environment file
Running the request gets me the response, as expected, and it's even saved in a log file for me.
Running the request with the slug set to ispum returns a valid response
The best thing about all this is that everything is a text file, so I can put my request file with its config in a folder in my repository, pushes it to my repository together with my source files and I suddenly have an easy way to interact with my API.
I hope that by this time you're convinced of the value of the Client π. But wait, we can do much more!
Generating full API Clients
Many of you are probably aware of the OpenAPI specification (formerly swagger files). It is basically a standard to describe your API. If you write a spec file for it, you basically write a "contract" for how your API works. The nice thing about it is that you can then use this contract to automagically generate a lot of things for your API! That can be clients, in the language of your choice, mock servers, or even documentation. There is a great project for this, conveniently called OpenAPI Generator.
During the Christmas holidays, I set myself on a quest to create a simple generator for the Jetbrains HTTP Client, which didn't exist yet. As of yesterday, it's released! Let's look through it with an example!
I'm a big DOTA 2 player, and let's imagine I want to start playing with the Open DOTA API. Conveniently, it provides us with an exhaustive OpenAPI specification file. Let's have a quick look
The beginning of the OpenAPI Spec file
As we can see, it describes the location of the API, as well as what kind of HTTP Calls you can make with the associated parameters.
Let's generate a full HTTP Client for it. First, we install the OpenAPI Generator
$ brew install openapi-generator
Then, we generate the client in a new folder
$ openapi-generator generate -i api.opendota.com/api -g jetbrains-http-client -o dotaClient
β¨That's it!β¨
When we open the folder in IntelliJ, we're presented with a few very nice things (Here is the GitHub repo if you want to see it for yourself):
- First, a complete documentation with all available endpoints and how they work in the README. Each call is clickable and link to locations in our source code.
A screenshot of the Dota Client README
Then, an Apis folder with all available calls and their related documentation. Here is the generated code for the
Heroes
endpoints :HeroesApi
GET /heroes
@name heroesGet
GET api.opendota.com/api/heroes
GET /heroes/{hero_id}/durations
@name heroesHeroIdDurationsGet
GET api.opendota.com/api/heroes{{hero_id}}/durations
GET /heroes/{hero_id}/itemPopularity
@name heroesHeroIdItemPopularityGet
GET api.opendota.com/api/heroes{{hero_id}}/itemPopularity
GET /heroes/{hero_id}/matches
@name heroesHeroIdMatchesGet
GET api.opendota.com/api/heroes{{hero_id}}/matches
GET /heroes/{hero_id}/matchups
@name heroesHeroIdMatchupsGet
GET api.opendota.com/api/heroes{{hero_id}}/matchups
GET /heroes/{hero_id}/players
@name heroesHeroIdPlayersGet
GET api.opendota.com/api/heroes{{hero_id}}/players
Now let's create en environment file and run all those calls for a given hero. Let's use the id
of my favourite hero : Crystal Maiden
.
{ "dev": { "hero_id": "5" } }
Just like this, I can start running queries against the API, and for example which players have the most wins with Crytal Maiden :
A screenshot of the results of running the Heroes to Player endpoint
I can also decide to run the Health endpoint and see whether the DOTA or Steam servers are down π
Results of running a query against the Health endpoint
Objective reached! I can play against external APIs locally, in source code, without having to use any tool like Postman and without going out of IntelliJ.
Adding tests
Running endpoints is nice, but we also want to be able to make sure expectations are fulfilled! Thankfully, there is a way to do this the the HTTP Request client :
GET /heroes
@name heroesGet
GET api.opendota.com/api/heroes
{% client.test("Request executed successfully", function() { // client.assert(response.status === 200, "Response status is not 200"); client.assert(response.body.length == 126, "DOTA 2 currently has 123 heroes"); }); %}
Creating a purposefully failing test
This should fail, as DOTA 2 currently has 136 heroes :
Bingo!
Going even further
I've always been a bit frustrated at Jetbrains for their HTTP Client, because it was completely bound to Intellij. As amazing as it is, I can't spend a lot of time crafting nice custom API calls if I still have to create tests for my CI anyways. At the end of the day, it's double the work for me.
But last month, they announced something that filled me with joy : A CI runner for the HTTP Request files! All of a sudden, all the work we did above became 1000x more useful!
Let's have a look into it :
We download the (preview, for now), tool and unzip it:
$ curl -f -L -o ijhttp.zip "jb.gg/ijhttp/latest $ unzip ijhttp.zip
Finally, we run it against the files we have generated above, with our environment file:
$ ./ijhttp/ijhttp Apis/HeroesApi.http --env-file Apis/http-client.env.json --env dev
Et voilΓ !
$./ijhttp/ijhttp Apis/HeroesApi.http --env-file Apis/http-client.env.json --env dev ξ² β β± 6s ο β± 10:23:37 ο βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β Running IntelliJ HTTP Client with β ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β Files β HeroesApi.http β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β Public Environment β hero_id = 5 β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β Private Environment β β ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββββ Request 'heroesGet' GET api.opendota.com/api/heroes Failed HeroesApi.http#0 Request executed successfully Request 'heroesHeroIdDurationsGet' GET api.opendota.com/api/heroes/5/durations Request 'heroesHeroIdItemPopularityGet' GET api.opendota.com/api/heroes/5/itemPopularity Request 'heroesHeroIdMatchesGet' GET api.opendota.com/api/heroes/5/matches Request 'heroesHeroIdMatchupsGet' GET api.opendota.com/api/heroes/5/matchups Request 'heroesHeroIdPlayersGet' GET api.opendota.com/api/heroes/5/players 6 requests completed, 1 have failed tests RUN FAILED
We still have our failing test, which will fail our CI. Task failed successfully!
The last step to have a complete setup is to setup a CI action. Let's use GitHub actions for this, fix our failing test, and see what happens:
name: Run API tests based on Jetbrains HTTP Client on: push jobs: build: runs-on: ubuntu-latest steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '17'
- name: Downloads and runs the Jetbrains HTTP Client CLI run: | curl -f -L -o ijhttp.zip "jb.gg/ijhttp/latest" unzip ijhttp.zip ./ijhttp/ijhttp Apis/HeroesApi.http --env-file Apis/http-client.env.json --env dev
We checkout, set the Java version to 17 and run the ijhttp
package like we did locally.
Success! We now have reliable CI tests using Jetbrains HTTP Client requests!
Going further
There is so much more I could talk about, like the fact that the HTTP Client supports other protocols like GraphQL, handles redirections and more, but I'll keep it to this for now.
Do keep in mind that the Jetbrains HTTP Client generator is still in a very early phase at the moment. For example, I have no support for authentication or most headers at the moment. It won't break anything, but you might have to add things manually to the generated files.
But hey, I do welcome requests and contributions!
Hope you found the read useful. Feel free to ping me on Mastodon or Twitter!