Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

APIs

apis.livemd

APIs

Mix.install([
  {:kino, github: "livebook-dev/kino", override: true},
  {:kino_lab, "~> 0.1.0-dev", github: "jonatanklosko/kino_lab"},
  {:vega_lite, "~> 0.1.4"},
  {:kino_vega_lite, "~> 0.1.1"},
  {:benchee, "~> 0.1"},
  {:ecto, "~> 3.7"},
  {:math, "~> 0.7.0"},
  {:faker, "~> 0.17.0"},
  {:httpoison, "~> 1.8"},
  {:poison, "~> 5.0"},
  {:utils, path: "#{__DIR__}/../utils"}
])

Navigation

Return Home Report An Issue

What Is An API?

API stands for Application Programming Interface. In broad terms, an API is a way to communicate between pieces of software.

Generally, API refers to web APIs, which are programs that run on the internet. The internet is a network of interconnected machines that know how to communicate with each other.

We won’t go deep into networking or how the internet works, as that is beyond the scope of this course. However, Crash Course Computer Science provides an excellent overview.

Kino.YouTube.new("https://www.youtube.com/watch?v=AEaKrq3SpW8")

Client-Server Model

These APIs use a client-server model. Servers provide a resource, and clients request a resource.

You use APIs every time you open your browser (Chrome, Safari, Firefox, Edge, etc.). The browser is the client, and it requests information from a server. For example, when you search for a video on YouTube, your browser communicates with YouTube servers to retrieve the video file.

URL (Uniform Resource Locator)

The client uses a URL (Uniform Resource Locator) to locate the server on the internet.

For example, you can visit the URL https://v2.jokeapi.dev/joke/Any?safe-mode&format=json. This URL locates the server(s) powering the Joke API that returns a random joke.

The Joke API returns a JSON response. JSON is a key-value format conceptually similar to a map in Elixir.

{
    "error": false,
    "category": "Programming",
    "type": "twopart",
    "setup": "Why did the functional programmer get thrown out of school?",
    "delivery": "Because he refused to take classes.",
    "flags": {
        "nsfw": false,
        "religious": false,
        "political": false,
        "racist": false,
        "sexist": false,
        "explicit": false
    },
    "id": 48,
    "safe": true,
    "lang": "en"
}

A URL contains the following information

  • The communication protocol: https
  • The domain name: v2.jokeapi.dev
  • Path (optional): joke/Any
  • Query parameters (optional): ?safe-mode&format=json

The communication protocol specifies how to communicate with the server. HTTP (Hypertext Transfer Protocol) and HTTPS (Hypertext Transfer Protocol Secure) are communication protocols used to send and receive data between the client and the server.

The domain name is a convenient label that maps to the underlying IP (Internet Protocol) address. For example, you can use http://142.250.217.68 instead of https://www.google.com to request the google home page from a google server.

The path specifies the resource we want from the server. In this case, we use the /joke/Any path to get a random joke.

Query parameters contain additional options and information to send to the server. A URL may include many query parameters to send to the server. Query parameters start with a question mark ? and are each split by an ampersand &. For example, ?safe-mode&format-json specifies that we want to use safe-mode and return the joke in a JSON format.

Web APIs

We use web APIs to communicate with servers on the internet. These APIs provide useful functionality that would otherwise be difficult to build on our own. Here are a few example APIs.

HTTP

We use HTTP to communicate with web APIs. The browser hides the details of how we use HTTP to communicate with these APIs. However, as developers, we want to interact with them directly using the HTTP protocol to request and send information.

In Elixir, we use an HTTP client to make HTTP requests. We’ve installed the popular HTTPoison HTTP client library for you in this livebook to demonstrate how to make HTTP requests.

With HTTPoison we can programmatically request information from the same Joke API you visited in the browser.

HTTPoison.get("https://v2.jokeapi.dev/joke/Any?safe-mode&format=json")

We use the HTTPoison.get/3 function to make a GET request. The response from the server includes the :body, :headers, :status_code, and :request_url.

The :body is the JSON response you saw previously in the browser, and :headers provide additional context and meta-data about the response. The :status_code specifies the response’s status, where 200 indicates a successful response. The :request_url is simply the URL.

Response Codes

APIs use various response codes to communicate the status of a request. These are generally called response codes and are grouped into five classes.

  1. Informational responses (100–199)
  2. Successful responses (200–299)
  3. Redirection messages (300–399)
  4. Client error responses (400–499)
  5. Server error responses (500–599)

You’ve already seen that a successful request returns a 200 response. You might also be familiar with the 404 not found response code.

{:ok, response} = HTTPoison.get("https://v2.jokeapi.dev/joke/Any?safe-mode&format=json")
response.status_code

JSON

To parse a JSON response into a more convenient data structure, we can use the Poison library. Poison.decode!/2 converts the string representation of the JSON into an Elixir value.

Poison.decode!("{\"key\"\: \"value\"}")

By combining HTTPoison and Poison we can retrieve the JSON from the Joke API and return an Elixir map.

response =
  case HTTPoison.get("https://v2.jokeapi.dev/joke/Any?safe-mode&format=json") do
    {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
      Poison.decode!(body)
  end

Now we could use this response in our program.

IO.puts("""
#{response["setup"]}

#{response["delivery"]}
""")

Your Turn

Use Poison.decode!/2 to decode the following json string.

string = "{\"hello\"\: \"world\"}"

HTTP GET and HTTP POST

HTTP GET requests retrieve data from a server and HTTP POST requests send data to a server.

We’ve already seen how we can use HTTPoison.get/3 to make an HTTP GET request. We can also use the HTTPoison.post/4 function to make an HTTP POST request.

POST requests include body and headers. The body contains the data to send to the server. The headers contain context and metadata about the body of data being sent. which is the data to send to the server. The POST request also includes headers which contain context and metadata about the data. For example, the Content-Type header tells the server the format of data being sent.

Below we send a POST request to http://httparrot.herokuapp.com/post. The body is a JSON string "{\"body\": \"test\"}" and the Content-Type header specifies that the data being sent is JSON.

HTTPoison.post("http://httparrot.herokuapp.com/post", "{\"body\": \"test\"}", [
  {"Content-Type", "application/json"}
])

Private APIs

So far, we’ve seen how to use HTTPoison and Poison to retrieve information from a public API. However, some APIs are private.

Private APIs require an API key to communicate with them. This key is then included in the HTTP request to authenticate the request.

For example, the Open Weather API requires an API key when making a GET request. The server returns a 401 unauthorized response without the correct API key.

api_key = ""

HTTPoison.get("https://api.openweathermap.org/data/2.5/weather?lat=35&lon=139&appid=#{api_key}")

Generally, you want to keep your API key a secret because bad actors could use it to maliciously access private information or attack the server with too many requests.

Your Turn

The Open Weather API exposes the following URL to provide weather data.

https://api.openweathermap.org/data/2.5/weather?lat=LATITUDE&lon=LONGITUDE&appid=API_KEY

lat, lon, and appid are all query parameters, where we replace LATITUDE, LONGITUDE, and API_KEY with the correct values.

Create a free account on the Open Weather API and then get your API key from Open Weather API Keys page.

Use your API key and valid latitude/longitude coordinates to make a successful request to their API. Ensure the :status_code is 200 rather than 401.

lat = 35
lon = 139
api_key = ""

{:ok, response} =
  HTTPoison.get(
    "https://api.openweathermap.org/data/2.5/weather?lat=#{lat}&lon=#{lon}&appid=#{api_key}"
  )

Once you are sure the above works correctly, use Poison.decode/2 to decode the response into an Elixir map.

response

Commit Your Progress

Run the following in your command line from the project folder to track and save your progress in a Git commit.

$ git add .
$ git commit -m "finish apis section"