Hurl 2.0.0, the GraphQL Edition
Christmas has been busy for the Hurl team! We’re happy to announce Hurl 2.0.0, with a lot of new features and bug fixes. If you don’t know Hurl, it’s a command line tool powered by curl that runs HTTP requests defined in a simple text format:
# Create a new cat:
POST https://example.org/api/cats
{
"name": "Kitty",
"color": "black"
}
HTTP 201
[Captures]
cat-id: jsonpath "$.id"
# Get our new cat:
GET https://example.org/api/cats/{{cat-id}}
HTTP 200
[Asserts]
jsonpath "$.lives" == 9
Hurl can be used to get data like curl, or as a testing tool for JSON/XML apis and HTML content.
So, what’s new in 2.0.0?
GraphQL Query Support
Hurl can now be used to send GraphQL query request very easily. The syntax is natural and use ```
body with a graphql
hint.
For instance, using GitHub GraphQL APIs, we can check the number of stars of a project:
POST https://api.github.com/graphql
Authorization: bearer {{token}}
```graphql
{
repository(owner: "Orange-OpenSource", name: "hurl") {
stargazerCount
}
}
```
HTTP 200
And this Hurl file can be run like this:
$ hurl --variable token=FOO github.hurl | jq
{
"data": {
"repository": {
"stargazerCount": 3578
}
}
}
It’s a very “Markdowny” syntax which re-use existing Hurl multiline body.
GraphQL queries, of course, can use GraphQL variables:
POST https://api.starwars.com/graphql
```graphql
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
variables {
"episode": "JEDI"
}
```
HTTP 200
And, as any multiline body, GraphQL queries can be templatized with Hurl variables!
POST https://api.starwars.com/graphql
```graphql
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
variables {
"episode": {{episode}}
}
```
HTTP 200
As GraphQL APIs return JSON, we can add asserts on the HTTP response:
POST https://api.starwars.com/graphql
```graphql
{
human(id: "1000") {
name
height
}
}
```
HTTP 200
[Asserts]
jsonpath "$.data.human.name" == "Luke Skywalker"
jsonpath "$.data.human.height" == 1.72
Processing Data with Filters
Sometimes you want to process response body before capturing data or adding tests.
Let’s say we have an endpoint that returns an HTTP header x-servers
containing a joined list of servers (for instance rec1.org,rec3.org
).
You want to test the value of this header: number of server, value of server 1 etc... You can now use filters
to process the response headers and add clean asserts:
GET https://example.org/api
HTTP 200
[Asserts]
header "x-servers" split "," count == 2
header "x-servers" split "," nth 0 == "rec1.org"
header "x-servers" split "," nth 1 == "rec3.org"
jsonpath "$.books" count == 12
In this sample, header "x-servers"
extracts the HTTP header from the response. Then, we have a chain of two filters
split "," nth 0
. The first filter split
turns the header to a list, and the second filter, nth
, returns an element of the list.
Then we have our final test with == "rec1.org"
.
Filters can also be applied on captures:
GET https://example.org/api
HTTP 200
[Captures]
name: jsonpath "$user.id" replace /\d/ "x" # Make our user anonymous
Let’s take a final example, with a live existing service that returns a CSV of stock exchanges https://csvbase.com/meripaterson/stock-exchanges
First, we split our response body by lines and save it in a variable lines
.
GET https://csvbase.com/meripaterson/stock-exchanges
HTTP 200
[Captures]
rows: body split "\n"
Then, we create two other variables australia
and algeria
that are row 7 and 18.
GET https://csvbase.com/meripaterson/stock-exchanges
HTTP 200
[Captures]
rows: body split "\n"
australia: variable "rows" nth 7 split ","
algeria: variable "rows" nth 18 split ","
Now that we have our row 7 and 18, we can add assert on specifics columns:
GET https://csvbase.com/meripaterson/stock-exchanges
HTTP 200
[Captures]
lines: body split "\n"
australia: variable "lines" nth 7 split ","
algeria: variable "lines" nth 18 split ","
[Asserts]
variable "australia" nth 1 == "Australia & Oceania"
variable "australia" nth 2 == "Australia"
variable "algeria" nth 1 == "Africa"
variable "algeria" nth 2 == "Algeria"
Et voilà !
Filters can be used to transform the HTTP response data easily. We have included a first round of filters count
,
htmlEscape
, htmlUnescape
, nth
, regex
, replace
, split
, toInt
, urlDecode
and urlEncode
. Don’t hesitate to give us some
feedbacks on this nice new feature!
Various curl Options
Under the hood, Hurl uses curl for HTTP transfers. We have implemented the following curl options: -E/--cert
, --connect-to
,
--key
, --resolve
and --ssl-no-revoke
. For instance, given this Hurl file hello.hurl
:
GET http://foo.com/hello
HTTP 200
`Hello World!`
GET http://bar.com/hello
HTTP 200
`Hello World!`
GET http://baz.com/hello
HTTP 200
`Hello World!`
You can force the resolution of foo.com
, bar.com
and baz.com
to localhost
with --connect-to
:
$ hurl --connect-to foo.com:80:localhost:8000 \
--connect-to bar.com:80:localhost:8000 \
--connect-to baz.com:80:localhost:8000 \
hello.hurl
Minor Syntax Changes
Astute readers have already spot some minor syntax changes. Instead of HTTP/*
for wildcard HTTP version test, you can simply
write HTTP
now (the first syntax still work but is deprecated)
# New Syntax
GET https://foo.com
HTTP 200
# Deprecated syntax
GET https://foo.com
HTTP/* 200
If you want to add precise tests on HTTP version, you can still use HTTP/1.0
, HTTP/1.1
and HTTP/2
:
GET https://foo.com
HTTP/2 200
GET https://bar.com
HTTP/1.1 200
GET https://baz.com
HTTP/1.0 200
Thanks to @jmoore34 for this idea, it’s obvious now you see it!
Finally, plain text body have been tweaked, to allow the use of a language hint like graphql
or json
:
-
Post a CSV:
POST https://example.com/csv ``` line1,red,1 line2,green,2 line3,green,3 ```
-
Use a GraphQL query
POST https://example.com/graphql ```graphql { human { name } } ```
-
Post a JSON body
POST https://example.com/json ```json { "name": "toto", "age": 18 } ```
-
Post a JSON body without
```
(this shorter syntax is prefered over the previous one)POST https://example.com/json { "name": "toto", "age": 18 }
-
And finally, post a single string without newline:
POST https://example.com/csv `Hello world!`
You may notice that the syntax takes huge inspiration from Markdown and that’s very intentional!
That’s All
There are other improvements and bug fixes, you can check a complete list in our release note. If you like Hurl, don’t hesitate to give us a star on GitHub or share it on Twitter!
We’ll be happy to hear from you, either for enhancement requests or for sharing your success story using Hurl!