Hello guys
I am currently studying elm and I came across an interesting problem…
I’m creating a pokedex using the pokeapi
The fact is that the api modeling is very verbose and complex
The scenario I want is the following:
- Search a pokemon by its name
- Then list its information (name, picture, description)
- And list its evolutions
The cenario is:
- First I have to fetch the pokemon
- https://pokeapi.co/api/v2/pokemon/inser-your-pokemon/
- Then with the response, I must get its specie (this is another request) and I get the url by accessing this property
pokemon.species.url
- Then I must request:
- /api/v2/pokemon-species/4/ (charmander specie page)
- THEN I must request its evolution chain to get all charmander evolutions and so on…
I currently came up with the below solution and I would like to know if there is a better way to do this.
I want you to focus on the request line in SearchPokemon
update
-- Api module
pokeDecoder : Decoder Pokemon
pokeDecoder =
Decode.succeed Pokemon
|> required "name" string
|> requiredAt [ "species", "url" ] string
specieDecoder : Decoder Specie
specieDecoder =
Decode.succeed Specie
|> requiredAt [ "genera", "2", "genus" ] string
|> requiredAt [ "evolution_chain", "url" ] string
getPokemon : String -> Task Http.Error Pokemon
getPokemon term =
let
url =
"https://pokeapi.co/api/v2/pokemon/" ++ term ++ "/"
in
Http.get url pokeDecoder
|> Http.toTask
getSpecie : Pokemon -> Task Http.Error Specie
getSpecie pokemon =
Http.get pokemon.specieUrl specieDecoder
|> Http.toTask
-- Update module
type Msg
= SearchPokemon
| PokeSpecieResponse (Result Http.Error ( Pokemon, Specie ))
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
SearchPokemon ->
let
request =
getPokemon (String.toLower model.searchInput)
in
( { model | loading = True }
-- Here is the tricky part
, request
|> Task.andThen
(\pokemon ->
Task.map (\specie -> ( pokemon, specie )) (getSpecie pokemon)
)
|> Task.attempt PokeSpecieResponse
)
PokeSpecieResponse (Ok ( pokemon, specie )) ->
( { model
| loading = False
, pokemon = Just pokemon
, specie = Just specie
}
, Cmd.none
)
PokeSpecieResponse (Err error) ->
( { model | loading = False, pokemon = Nothing }, Cmd.none )
I’m omitting some of the complexity, but if for example I want to also search the evolutions, the request is gonna grow by something like this:
request
|> Task.andThen
(\pokemon ->
Task.map (\specie -> ( pokemon, specie )) (getSpecie pokemon)
)
|> Task.andThen
(\( pokemon, specie ) ->
Task.map (\evolution -> ( pokemon, specie, evolution )) (getEvolutionChain specie)
)
|> Task.attempt PokeSpecieResponse
My problem is:
- I need to request pokemon, species and evolutions
- So it’s 3 requests that are dependent, if one fail I want the whole process to fail
- Every task chain, I must keep the latest request result value
- So at the end I pass a command to update the model
This code works, but for me to delve into the evolutions this gets even more compelling, since pokeapi returns a grotesque json and I would have to deal, and this code tends to be a mess.
And that’s it >_<, I’m doing this for fun, but this case may appear in the real world
Thaaanks a lot for your attention =D