Intro
Often in software development, a team has to write API clients, make API requests in application code, and troubleshoot APIs that are necessary for the team's operation. Often these HTTP requests rely on multiple requests -- maybe to an Auth server first -- and multiple secrets. I have seen teams share Postman collections to share information about how to get the required information from the API when it isn't always obvious how to do so. I will show you how to use Hurl to do these tasks.
The Basics
Hurl gives you two command-line tools, hurl
and hurlfmt
. You will be producing Hurl files (.hurl extensions)
that you can share with your team to give information on how to use HTTP servers (REST, GraphQL, HTML) to get important information.
You can use hurlfmt
to make sure the file parses, as well as to produce simple documentation.
Documenting Complex Request Flows
Documenting complex request flows with multiple pieces of information (OAUTH client IDs and secrets, Basic Auth usernames and passwords, API Keys) and multiple requests is a perfect use-case for Hurl. For this blog post, I am using the Hurl version (hurl 4.2.0-SNAPSHOT) to have support for variables in keys PR #2012.
Suppose you are making requests to API #1, which requires you to authenticate against another endpoint and then use a bearer token from that endpoint to make the requests against API #1. Here is what that looks like in a Hurl file:
POST https://auth.example.com/authenticate/
X-API-REASON: "authentication"
[Options]
verbose: true
{
"OAUTH_CLIENT_ID": "{{OAUTH_CLIENT_ID}}",
"OAUTH_CLIENT_SECRET": "{{OAUTH_CLIENT_SECRET}}"
}
HTTP 200
[Captures]
token: jsonpath "$['token']"
GET https://api1.example.com/v1/items
Authorization: "Bearer {{token}}"
[Options]
verbose: true
To understand the Hurl file, look for a Request section and an optional Response section. The file has two Request sections and one Response section. What is nice about Hurl files is that you can add comments to call out pieces of information that are especially important.
POST https://auth.example.com/authenticate/ # POST to the Auth server
X-API-REASON: "authentication" # Header that is specific to this Auth server
[Options]
verbose: true # Turn off verbose if you don't want to see Request/Response information
{
"OAUTH_CLIENT_ID": "{{OAUTH_CLIENT_ID}}",
"OAUTH_CLIENT_SECRET": "{{OAUTH_CLIENT_SECRET}}"
}
#^^^^^ The JSON body to the POST request
HTTP 200 # A HTTP Status code that you are asserting/expecting
[Captures] # What you are capturing from the Response
token: jsonpath "$['token']"
GET https://api1.example.com/v1/items # The GET to API #1 to find your items
Authorization: "Bearer {{token}}" # Using the captured variable in a header
[Options]
verbose: true # Turn off if you don't want to see the Request/Response information
This file requires two pieces of information: the OAUTH_CLIENT_ID and the OAUTH_CLIENT_SECRET. These can be handled in many different ways.
Some ways to handle variables in Hurl files
--variable
hurl --variable OAUTH_CLIENT_ID=foo --variable OAUTH_CLIENT_SECRET=bar file.hurl
--variables-file
create an env file (prod.env):
OAUTH_CLIENT_ID=foo
OAUTH_CLIENT_SECRET=bar
and
hurl --variables-file prod.env file.hurl
From Environment Variables
export HURL_OAUTH_CLIENT_ID=foo
export HURL_OAUTH_CLIENT_SECRET=bar
hurl file.hurl
Sharing a Hurl file with a teammate
If someone within the team runs the file without including the variables that are needed hurl
will give them a helpful error message.
❯ hurl file.hurl
* ------------------------------------------------------------------------------
* Executing entry 1
*
* Entry options:
* verbose: true
error: Undefined variable
--> session5.hurl:6:27
|
6 | "OAUTH_CLIENT_ID": "{{OAUTH_CLIENT_ID}}",
| ^^^^^^^^^^^^^^^ you must set the variable OAUTH_CLIENT_ID
Adhoc use of an API
Sometimes team members want one piece of information from a server -- maybe to determine what a probable value would look like, maybe because the API doesn't have an OpenAPI schema and so they are unsure what the output should be -- and so they want to make one or two HTTP requests.
For this example, I'm going to use the Nobel Prize public API.
Create a file sess.hurl:
GET https://api.nobelprize.org/v1/laureate.{{format}}
[QueryStringParams]
id: {{ NOBEL_WINNER_ID }}
Then to run this file:
hurl --variable NOBEL_WINNER_ID=5 --variable format=json sess.hurl | jq
The result is the JSON object representing Pierre Curie.
Conclusion
Hurl can be used for information sharing within teams around complex API workflows.