Let’s try to monitor crypto assets through the Coinmarketcap API, with Elixir!
Topics I want to explore in this exercise are:
- Elixir Supervision trees
- HTTP calls with Elixir (with HTTPoison)
- TDD and Refactoring
Get crypto assets quotes
Example with cURL
First things first. Are we able to make a simple (authenticated) HTTP call to the coinmarketcap API? I surely think so.
In curl this would be like this (use your own API Key):
curl -H "X-CMC_PRO_API_KEY: YOUR_COINMARKETCAP_API_KEY" -H "Accept: application/json" -d "symbol=BTC" -d "convert=EUR" -G https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest
The command above contacts the Coinmarketcap API to get the latest quote (conversion rate) of Bitcoin related to FIAT EUR.
Note: this is a contrived example, as there is another endpoint (namely /v1/cryptocurrency/listings/latest
) to get multiple quotes in a single API call
The response looks like this:
{
"status": {
"timestamp": "2020-04-28T06:46:09.890Z",
"error_code": 0,
"error_message": null,
"elapsed": 14,
"credit_count": 1,
"notice": null
},
"data": {
"BTC": {
"id": 1,
"name": "Bitcoin",
"symbol": "BTC",
"slug": "bitcoin",
"num_market_pairs": 7965,
"date_added": "2013-04-28T00:00:00.000Z",
"tags": [
"mineable"
],
"max_supply": 21000000,
"circulating_supply": 18349112,
"total_supply": 18349112,
"is_active": 1,
"platform": null,
"cmc_rank": 1,
"is_fiat": 0,
"last_updated": "2020-04-28T02:45:58.000Z",
"quote": {
"EUR": {
"price": 7175.97279154951,
"volume_24h": 32035162374.6573,
"percent_change_1h": 0.07515773,
"percent_change_24h": 0.45923884,
"percent_change_7d": 12.62102123,
"market_cap": 131672728461.0946,
"last_updated": "2020-04-28T06:45:04.000Z"
}
}
}
}
}
One could say: “Bravo Christian, you can use curl with a copy-pasted example. What about Elixir?”
Example with HTTPoison
Add the dependency HTTPoison
to your mix.exs (mix new YOUR_APP_NAME
if you’re not in an existing application):
...
defp deps do
[
{:httpoison, "~> 1.6"}
]
end
...
Use iex -S mix
to enter an interactive shell:
HTTPoison.get! "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest"
This results in a bad response, since the API Key is missing.
Let’s see the documentation for “HTTPoison.get!” with h
:
For more information about h
see IEx Helpers.
h HTTPoison.get!
Yielding:
def get!(url, headers \\ [], options \\ [])
@spec get!(binary(), headers(), Keyword.t()) ::
HTTPoison.Response.t() | HTTPoison.AsyncResponse.t()
Issues a GET request to the given url, raising an exception in case of failure.
If the request does not fail, the response is returned.
See request!/5 for more detailed information.
Now that we know that HTTPoison.get!
takes a second argument, namely the headers, let’s try to pass them to the function:
headers = ["X-CMC_PRO_API_KEY": "YOUR_API_KEY", "Accept": "Application/json; Charset=utf-8"]
HTTPoison.get! "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest", headers
Almost! We’re missing some required fields, namely id
, symbol
or slug
:
headers = ["X-CMC_PRO_API_KEY": "YOUR_API_KEY", "Accept": "Application/json; Charset=utf-8"]
HTTPoison.get! "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol=BTC&convert=EUR", headers
Finally! A %HTTPoison.Response
struct is returned with the property body.
Let’s test it
Found this little gem about Elixir Test Mocking with Mox.
The general advice is:
Define a behaviour for the CoinmarketcapClient
(read more about Elixir behaviours) so that only during tests, a mock behaviour is used for testing purposes.