Talking to Web Services
Most backends talk to other services: an exchange rate service, a payment provider, a delivery company. They do it with web requests, and the answers usually come back as JSON. Quill’s http module sends them.
bring http
permit net "api.example.com"
type Rates
usd_to_eur: number
answer = fetch("https://api.example.com/rates")
on NetworkDown -> say "The rate service can't be reached."
when
answer.status == 200 -> say "Got the rates."
else -> say "The service answered {answer.status}."
rates = answer.body as Rates
on Invalid -> Rates usd_to_eur = 0
say convert 100 USD to EUR at rates.usd_to_eur
Each part has a job:
bring httpbrings in Quill’shttpmodule, so a reader sees at the top that the program uses the network.permit net "api.example.com"lets the program talk to that one host, and no other. Without it, the call is refused (see Permits).fetch(url)asks for the page aturland gives back an answer: a record withstatus, a number like200or404, andbody, the text that came back.as Ratesreads the body as a type, checking every field (see JSON).
The examples in this chapter talk to services on the internet, so the book shows them without running them.
When a request goes wrong
There are two different ways for a request to go wrong, and Quill keeps them apart:
- The service can’t be reached: no network, a wrong host name, or no answer within 30 seconds. That’s the failure
NetworkDown, handled withon NetworkDownortry, like any failure. - The service answers with an error, like
404(not found) or500(the service broke). That’s still an answer. Itsstatussays what happened, and your program decides what to do.
Check the status before trusting the body. 200 to 299 mean it worked.
Sending data: post and send
post(url, body) sends text to a service, usually JSON made with json(...). It says the body is JSON for you:
bring http
permit net "api.example.com"
type Order
item: text
amount: USD
order = Order item = "Bamenda Brass", amount = 150000 USD
reply = post("https://api.example.com/orders", json(order))
on NetworkDown -> say "Couldn't send the order."
say reply.status
send(method, url, body, headers) sends any kind of request, with your own headers. Each header is text with a name, a colon, and a value. A key from a setting goes in a header, not in the code:
bring http
permit net "api.example.com"
permit env "SHOP_KEY"
fn cancel_order(key: text) -> number
reply = send("DELETE", "https://api.example.com/orders/7", "", list "Authorization: Bearer {key}")
on NetworkDown -> return 0
return reply.status
when env("SHOP_KEY")
found key -> say cancel_order(key)
missing -> say "Set SHOP_KEY first."
The methods are GET, POST, PUT, PATCH, DELETE and HEAD. Use "" for no body.
Common mistakes
A program has to bring the module before it uses it:
permit net "api.example.com"
answer = fetch("https://api.example.com/rates")
on NetworkDown -> say "offline"
Problem on line 3 of example.quill:
answer = fetch("https://api.example.com/rates")
^^^^^
fetch is part of Quill's http module, which this program doesn't bring.
Add bring http at the top, so a reader sees what the program uses.
Did you mean: bring http
Without a permit for the host, the call is refused before the program runs:
bring http
answer = fetch("https://api.example.com/rates")
on NetworkDown -> say "offline"
Problem on line 3 of example.quill:
answer = fetch("https://api.example.com/rates")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fetch can't reach https://api.example.com/rates, because the program has no permit for it.
A program touches only what its permits allow. If it should, add this at the top of the program.
Did you mean: permit net "api.example.com"
A web address starts with https:// or http://:
bring http
permit net "api.example.com"
answer = fetch("api.example.com/rates")
on NetworkDown -> say "offline"
Problem on line 4 of example.quill:
answer = fetch("api.example.com/rates")
^^^^^^^^^^^^^^^^^^^^^^^
api.example.com/rates isn't a web address.
A web address starts with https:// or http://, like https://api.example.com/rates.
Try it
- Write a program that brings
http, permits one host, and fetches a page from it. Say the status. - Remove the permit and read what Quill says.
- Fetch a page that doesn’t exist on that host, and handle the
404withwhen. - Make a record, and send it with
post(url, json(record))to a test service likehttps://httpbin.org/post.