Using Hurl instead of Postman for information sharing within teams

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.