Powered by AppSignal & Oban Pro

Pokemon API

exercises/pokemon_api.livemd

Pokemon API

Mix.install([
  {:jason, "~> 1.4"},
  {:kino, "~> 0.8.0", override: true},
  {:youtube, github: "brooklinjazz/youtube"},
  {:hidden_cell, github: "brooklinjazz/hidden_cell"},
  {:httpoison, "~> 1.8"},
  {:poison, "~> 5.0"}
])

Navigation

Return Home Report An Issue

Mastery

Mastery assignments are bonus projects designed to incorporate curriculum concepts at an advanced level. You have complete freedom with how you complete mastery assignments, and may change requirements as you see fit. Each mastery assignment serves as an independent project you may choose to include on your portfolio of projects.

Pokemon API

When we rely on an external API, it’s possible for them to change the format of a response, and have that cause issues for our application.

That’s why it’s often important to translate information from an API into a struct when we retrieve it. This way if the format of the API response changes then we can fix the issue in a single spot where we convert the API response into a struct. We can also keep only the information we want, rather than all of the data returned.

Often, certain APIs will have a language-specific implementation that makes it easier to interact with the API.

You’re going to create a Pokelixir project which uses the Pokemon API to return Elixir-friendly Pokemon structs.

Create a new Pokelixir mix project.

mix new pokelixir

Ensure you add HTTPoison and Poison as dependencies.

Pokemon Structs

A Pokemon struct should have the following (required) keys. You do not need to implement data validation.

classDiagram
  class Pokemon {
    id: integer
    name: string
    hp: integer
    attack: integer
    defense: integer
    special_attack: integer
    special_defense: integer
    speed: integer
    weight: integer
    height: integer
    types: list of strings
  }

Here’s that data represented as a Pokemon struct.

%Pokemon{
  id: 6,
  name: "charizard",
  hp: 78,
  attack: 84,
  defense: 78,
  special_attack: 109,
  special_defense: 85,
  speed: 100,
  height: 17,
  weight: 905,
  types: ["fire", "flying"]
}

Get A Pokemon

We can request information about Pokemon using the Pokemon API.

You can retrieve information with the following URL where name is the name of the pokemon.

https://pokeapi.co/api/v2/pokemon/name

For example, you can retrieve the stats for charizard by making an HTTP GET request to the following URL.

https://pokeapi.co/api/v2/pokemon/charizard

You should be able to retrieve the data for a pokemon and return it as a Pokemon struct.

Pokelixir.get("charizard")

Hint

You can retrieve and decode the response using HTTPoison and Poison.

{:ok, response} = HTTPoison.get("https://pokeapi.co/api/v2/pokemon/charizard")
response = Poison.decode!(response.body)

Get All Pokemon

You can retrieve a list of pokemon using the following URL.

https://pokeapi.co/api/v2/pokemon

By default, this URL only shows the the first 20 pokemon, and provides a "next" URL to retrieve the next 20 pokemon. This is a common pagination strategy to avoid loading too much data at once.

Response Example

{
  "count": 1154,
  "next": "https://pokeapi.co/api/v2/pokemon?offset=20&limit=20",
  "previous": null,
  "results": [
    {
    "name": "bulbasaur",
    "url": "https://pokeapi.co/api/v2/pokemon/1/"
    },
    {
    "name": "ivysaur",
    "url": "https://pokeapi.co/api/v2/pokemon/2/"
    },
    {
    "name": "venusaur",
    "url": "https://pokeapi.co/api/v2/pokemon/3/"
    },
    {
    "name": "charmander",
    "url": "https://pokeapi.co/api/v2/pokemon/4/"
    },
    {
    "name": "charmeleon",
    "url": "https://pokeapi.co/api/v2/pokemon/5/"
    },
    {
    "name": "charizard",
    "url": "https://pokeapi.co/api/v2/pokemon/6/"
    },
    {
    "name": "squirtle",
    "url": "https://pokeapi.co/api/v2/pokemon/7/"
    },
    {
    "name": "wartortle",
    "url": "https://pokeapi.co/api/v2/pokemon/8/"
    },
    {
    "name": "blastoise",
    "url": "https://pokeapi.co/api/v2/pokemon/9/"
    },
    {
    "name": "caterpie",
    "url": "https://pokeapi.co/api/v2/pokemon/10/"
    },
    {
    "name": "metapod",
    "url": "https://pokeapi.co/api/v2/pokemon/11/"
    },
    {
    "name": "butterfree",
    "url": "https://pokeapi.co/api/v2/pokemon/12/"
    },
    {
    "name": "weedle",
    "url": "https://pokeapi.co/api/v2/pokemon/13/"
    },
    {
    "name": "kakuna",
    "url": "https://pokeapi.co/api/v2/pokemon/14/"
    },
    {
    "name": "beedrill",
    "url": "https://pokeapi.co/api/v2/pokemon/15/"
    },
    {
    "name": "pidgey",
    "url": "https://pokeapi.co/api/v2/pokemon/16/"
    },
    {
    "name": "pidgeotto",
    "url": "https://pokeapi.co/api/v2/pokemon/17/"
    },
    {
    "name": "pidgeot",
    "url": "https://pokeapi.co/api/v2/pokemon/18/"
    },
    {
    "name": "rattata",
    "url": "https://pokeapi.co/api/v2/pokemon/19/"
    },
    {
    "name": "raticate",
    "url": "https://pokeapi.co/api/v2/pokemon/20/"
    }
  ]
}

Each pokemon in the results list also has a URL you can use to get information about each pokemon.

https://pokeapi.co/api/v2/pokemon/6/

You should be able to return a list of Pokemon structs for every pokemon.

Pokemon.all()

Hint

You can either recursively retrieve the next 20 pokemon using the "next" url, or you can make a request using the limit query parameter with a larger value than the number of pokemon.

{:ok, response} = HTTPoison.get("https://pokeapi.co/api/v2/pokemon/?limit=1154")
response = Poison.decode!(response.body)

GitHub Repository

Connect your Pokelixir project to a GitHub repository and create a README to describe the purpose of the project and what you learned building it.

Consider refactoring, adding documentation, and testing if you have not already to make this an even better resume project.

Further Feature Ideas

This project is designed to be extendable to improve your skills. Here are some examples of how to expand the initial feature set.

  • Add a cache which caches each pokemon to retrieved to avoid re-requesting the same data from the API.
  • Add more endpoints from the Pokemon API. see: https://pokeapi.co/docs/v2.
  • Add more information to the Pokemon struct to make it more closely represent the data from the Pokemon API.
  • Add documentation + doctests and publish your project on hex.pm

Mark As Completed

file_name = Path.basename(Regex.replace(~r/#.+/, __ENV__.file, ""), ".livemd")

save_name =
  case Path.basename(__DIR__) do
    "reading" -> "pokemon_api_reading"
    "exercises" -> "pokemon_api_exercise"
  end

progress_path = __DIR__ <> "/../progress.json"
existing_progress = File.read!(progress_path) |> Jason.decode!()

default = Map.get(existing_progress, save_name, false)

form =
  Kino.Control.form(
    [
      completed: input = Kino.Input.checkbox("Mark As Completed", default: default)
    ],
    report_changes: true
  )

Task.async(fn ->
  for %{data: %{completed: completed}} <- Kino.Control.stream(form) do
    File.write!(
      progress_path,
      Jason.encode!(Map.put(existing_progress, save_name, completed), pretty: true)
    )
  end
end)

form

Commit Your Progress

Run the following in your command line from the curriculum folder to track and save your progress in a Git commit. Ensure that you do not already have undesired or unrelated changes by running git status or by checking the source control tab in Visual Studio Code.

$ git checkout -b pokemon-api-exercise
$ git add .
$ git commit -m "finish pokemon api exercise"
$ git push origin pokemon-api-exercise

Create a pull request from your pokemon-api-exercise branch to your solutions branch. Please do not create a pull request to the DockYard Academy repository as this will spam our PR tracker.

DockYard Academy Students Only:

Notify your teacher by including @BrooklinJazz in your PR description to get feedback. You (or your teacher) may merge your PR into your solutions branch after review.

If you are interested in joining the next academy cohort, sign up here to receive more news when it is available.

Up Next

Previous Next
Spoonacular Recipe API Web Servers