Search code examples
elm

Elm: How to use data from one HTTP request in subsequent requests


I am new to Elm and just read the docs (https://guide.elm-lang.org/). I am modifying an example from there and playing around. What I want to do is to hit an endpoint which will give me a list of IDs. Later I want to hit another endpoint with each of these IDs and display the results.

https://hacker-news.firebaseio.com/v0/topstories.json - This endpoint has a list of IDs.

https://hacker-news.firebaseio.com/v0/item/[ID].json - This endpoint will give the details of the story of given ID.

With what I have till now, I can get the list of all IDs separately and I can get each story separately (hard-coded ID) and display them. But what I am trying achieve here is to

  • get the list of IDs (500 of them) from endpoint 1
  • get first 5 of the stories by hitting endpoint 2
  • have a "load more" button which will load 5 more and so on

I am not sure how to do this. Any help is greatly appreciated.

Thanks


Solution

  • You can fire the second request when you handle the response from the first endpoint. Something like:

    type Msg
        = GotIds (Result Http.Error (List Int))
        | GotStory (Result Http.Error (String))
    
    
    update : Msg -> Model -> (Model,  Cmd Msg)
    update msg model =
        case msg of
            GotIds result ->          
                case result of
                    Ok (first::rest) -> 
                        ({ model | ids = first::rest }, getStory first)
                    Ok _ ->
                        (model, Cmd.none)
                    Err _ ->
                        ({ model | story = "ERROR"}, Cmd.none)
            GotStory result ->    
                ({model | story = Result.withDefault "None" result}, Cmd.none)
    

    If you want to fire multiple Cmd at the same time, you can use Cmd.batch

    Here is an Ellie that gets the ids from the first request and then fetches the title for the first ID.

    You will want to create a custom type and decoder for each post.

    For posterity's sake, here is all of the code from the Ellie:

    module Main exposing (main)
    
    import Browser
    import Html exposing (Html, button, div, text)
    import Html.Events exposing (onClick)
    import Http
    import Json.Decode exposing (Decoder, field, int, list, string )
    
    type alias Model =
        { ids : List Int 
        , story : String
        }
    
    
    initialModel : Model
    initialModel =
        { ids = [] 
        , story = "None"
        }
    
    
    type Msg
        = GotIds (Result Http.Error (List Int))
        | GotStory (Result Http.Error (String))
    
    
    update : Msg -> Model -> (Model,  Cmd Msg)
    update msg model =
        case msg of
            GotIds result ->          
                case result of
                    Ok (first::rest) -> 
                        ({ model | ids = first::rest }, getStory first)
                    Ok [] ->
                        (model, Cmd.none)
                    Err _ ->
                        ({ model | story = "ERROR"}, Cmd.none)
            GotStory result ->    
                ({model | story = Result.withDefault "None" result}, Cmd.none)
    
    
    view : Model -> Html Msg
    view model =
        div []
            [ text model.story
            ]
    
    
    main : Program () Model Msg
    main =
        Browser.element
            { init = init
            , view = view
            , update = update
            , subscriptions = (\_ -> Sub.none)
            }
            
    init : () -> (Model, Cmd Msg)
    init flags =
        (initialModel, getIds)
            
    getIds : Cmd Msg
    getIds =
      Http.get
        { url = "https://hacker-news.firebaseio.com/v0/topstories.json"
        , expect = Http.expectJson GotIds (list int)
        }
    
    getStory : Int ->  Cmd Msg
    getStory id =
      Http.get
        { url = "https://hacker-news.firebaseio.com/v0/item/" ++ String.fromInt id ++ ".json"
        , expect = Http.expectJson GotStory (field "title" string)
        }