For quite some time now, my go-to tool for testing REST API requests has been Postman.
Each development team that builds an API service manages their own Postman Collection, which they subsequently share with me on a regular basis.
One of my biggest issues with this approach is that every time a team changes the API, I need to import the updated collection.
While Postman offers a service through which teams can share collections, this operates by placing a given collection outside the project’s version control system. As such, it becomes an external dependency.
REST Client to the rescue
A few weeks ago, I discovered the REST Client Visual Studio Code plugin that was created by Huachao Mao.
This plugin has a ton of awesome features that developers can use to send HTTP requests in Visual Studio Code.
The most important feature for me is .http
and .rest
file extension support for the HTTP language with syntax highlight support. This enables me to store plain text files that contain HTTP commands within the API git repository. As such, I can store the documentation, tests, and actual code in the same place.
Example
Let’s create an example based on the Star Wars API.
If we have the following code in the file starwars.http,
Visual Studio Code will understand we are trying to make an HTTP call to https://swapi.co/api/people/?search=luke
and will display a send request
link above the text:
# @name search
GET https://swapi.co/api/people/?search=luke HTTP/1.1
content-type: application/json
This call will return a JSON object within Visual Studio Code that is similar to that provided below. This allows us to save the file content with headers or the content body only.
{
"count": 1,
"results": [
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"birth_year": "19BBY",
"gender": "male",
"homeworld": "https://swapi.co/api/planets/1/",
"films": [
"https://swapi.co/api/films/2/"
],
"species": [
"https://swapi.co/api/species/1/"
],
"vehicles": [
"https://swapi.co/api/vehicles/14/"
],
"starships": [
"https://swapi.co/api/starships/12/"
],
"created": "2014-12-09T13:50:51.644000Z"
}
]
}
If we would like to fetch the homeworld for Luke Skywalker, we will need to issue a new GET call to https://swapi.co/api/planets/1
. It would be cool if we could use this return data to populate the new HTTP request.
Named requests
Thankfully, the REST Client offers support for Named requests by putting # @name requestName
just before the desired request URL. You can think of the request variable as attaching name metadata to the underlying request. Requests of this nature can be called using Named Request, while normal requests can be called with Anonymous Request.
Other requests can use requestName
as an identifier to reference the expected part of the named request or its latest response.
It’s important to note that if you want to refer the response of a named request, you need to manually trigger the named request to first retrieve its response.
The reference syntax of a request variable is a bit more complex than alternative forms of custom variables.
The request variable reference syntax is as follows: {{requestName.(response|request).(body|headers).(*|JSONPath|XPath|Header Name)}}
.
You have two reference part choices for the response or request: body and headers.
For the body part, you can use * to reference the full response body, and for JSON and XML responses, you can use JSONPath and XPath to extract a specific property or attribute. For example, if a JSON response returns body {"id": "mock"}
,
you can set the JSONPath part to $.id
to reference the id.
For the headers part, you can specify the header name, which is case insensitive, to extract the header value.
Let’s see how to use named requests. We’ll get the homeworld information from the API by utilizing the search request response body and subsequently extract the homeworld
value from the results
array.
# @name planet
GET {{search.response.body.$.results[0].homeworld}} HTTP/1.1
Content-Type: application/json
This request will only work if you have first performed a search request. It will return a JSON object that looks like the following:
{
"name": "Tatooine",
"rotation_period": "23",
"orbital_period": "304",
"climate": "arid",
"gravity": "1 standard",
"terrain": "desert",
"surface_water": "1",
"population": "200000",
"residents": [
"https://swapi.co/api/people/1/"
],
"films": [
"https://swapi.co/api/films/5/"
],
"created": "2014-12-09T13:50:49.641000Z"
}
We can perform a similar action to ascertain what kind of spices Luke Skywalker is. Again, we’ll use the search request body:
# @name species
GET {{search.response.body.$.results[0].species[0]}} HTTP/1.1
Content-Type: application/json
This request will return the following JSON object:
{
"name": "Human",
"classification": "mammal",
"designation": "sentient",
"average_height": "180",
"skin_colors": "caucasian, black, asian, hispanic",
"hair_colors": "blonde, brown, black, red",
"eye_colors": "brown, blue, green, hazel, grey, amber",
"average_lifespan": "120"
}
You can find the full starwars.http
file as a Gist on my Github page, which is located here: