dillonkearns/elm-pages/1.1.2

NOTE: the latest version is 12.3.0

Pages.StaticHttp

StaticHttp requests are an alternative to doing Elm HTTP requests the traditional way using the elm/http package.

The key differences are:

  • StaticHttp.Requests are performed once at build time (Http.Requests are performed at runtime, at whenever point you perform them)
  • StaticHttp.Requests strip out unused JSON data from the data your decoder doesn't touch to minimize the JSON payload
  • StaticHttp.Requests can use Pages.Secrets to securely use credentials from your environemnt variables which are completely masked in the production assets.
  • StaticHttp.Requests have a built-in StaticHttp.andThen that allows you to perform follow-up requests without using tasks

Scenarios where StaticHttp is a good fit

If you need data that is refreshed often you may want to do a traditional HTTP request with the elm/http package. The kinds of situations that are served well by static HTTP are with data that updates moderately frequently or infrequently (or never). A common pattern is to trigger a new build when data changes. Many JAMstack services allow you to send a WebHook to your host (for example, Netlify is a good static file host that supports triggering builds with webhooks). So you may want to have your site rebuild everytime your calendar feed has an event added, or whenever a page or article is added or updated on a CMS service like Contentful.

In scenarios like this, you can serve data that is just as up-to-date as it would be using elm/http, but you get the performance gains of using StaticHttp.Requests as well as the simplicity and robustness that comes with it. Read more about these benefits in this article introducing StaticHttp requests and some concepts around it.

Scenarios where StaticHttp is not a good fit

  • Data that is specific to the logged-in user
  • Data that needs to be the very latest and changes often (for example, sports scores)
type alias Request value =
Request value

A Request that will be made a build time. Multiple StaticHttp.Requests can be combined together using the mapN functions, very similar to how you can manipulate values with Json Decoders in Elm.

type alias RequestDetails =
{ url : String
, method : String
, headers : List ( String, String )
, body : Body
}

The full details to perform a StaticHttp request.

get : Value String -> Decoder a -> Request a

A simplified helper around StaticHttp.request, which builds up a StaticHttp GET request.

import Json.Decode as Decode exposing (Decoder)
import Pages.StaticHttp as StaticHttp


getRequest : StaticHttp.Request Int
getRequest =
    StaticHttp.get "https://api.github.com/repos/dillonkearns/elm-pages"
        (Decode.field "stargazers_count" Decode.int)
request : Value RequestDetails -> Decoder a -> Request a

Build a StaticHttp request (analagous to Http.request). This function takes in all the details to build a StaticHttp request, but you can build your own simplified helper functions with this as a low-level detail, or you can use functions like StaticHttp.get.

map : (a -> b) -> Request a -> Request b

Transform a request into an arbitrary value. The same underlying HTTP requests will be performed during the build step, but mapping allows you to change the resulting values by applying functions to the results.

A common use for this is to map your data into your elm-pages view:

import Json.Decode as Decode exposing (Decoder)
import Pages.StaticHttp as StaticHttp


view =
    StaticHttp.get "https://api.github.com/repos/dillonkearns/elm-pages"
        (Decode.field "stargazers_count" Decode.int)
        |> StaticHttp.map
            (\stars ->
                { view =
                    \model viewForPage ->
                        { title = "Current stars: " ++ String.fromInt stars
                        , body = Html.text <| "⭐️ " ++ String.fromInt stars
                        , head = []
                        }
                }
            )
succeed : a -> Request a

This is useful for prototyping with some hardcoded data, or for having a view that doesn't have any StaticHttp data.

import Pages.StaticHttp as StaticHttp


view :
    List ( PagePath Pages.PathKey, Metadata )
    ->
        { path : PagePath Pages.PathKey
        , frontmatter : Metadata
        }
    ->
        StaticHttp.Request
            { view : Model -> View -> { title : String, body : Html Msg }
            , head : List (Head.Tag Pages.PathKey)
            }
view siteMetadata page =
    StaticHttp.succeed
        { view =
            \model viewForPage ->
                mainView model viewForPage
        , head = head page.frontmatter
        }

Building a StaticHttp Request Body

The way you build a body is analagous to the elm/http package. Currently, only emptyBody and stringBody are supported. If you have a use case that calls for a different body type, please open a Github issue and describe your use case!

type alias Body =
Body

A body for a StaticHttp request.

emptyBody : Body

Build an empty body for a StaticHttp request. See elm/http's Http.emptyBody.

stringBody : String -> Body

Builds a string body for a StaticHttp request. See elm/http's Http.stringBody.

Chaining Requests

andThen : (a -> Request b) -> Request a -> Request b

Build off of the response from a previous StaticHttp request to build a follow-up request. You can use the data from the previous response to build up the URL, headers, etc. that you send to the subsequent request.

import Json.Decode as Decode exposing (Decoder)
import Pages.StaticHttp as StaticHttp


licenseData : StaticHttp.Request String
licenseData =
    StaticHttp.get
        (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
        (Decode.at [ "license", "url" ] Decode.string)
        |> StaticHttp.andThen
            (\licenseUrl ->
                StaticHttp.get (Secrets.succeed licenseUrl) (Decode.field "description" Decode.string)
            )
resolve : Request (List (Request value)) -> Request (List value)

Helper to remove an inner layer of Request wrapping.

combine : List (Request value) -> Request (List value)

Turn a list of StaticHttp.Requests into a single one.

import Json.Decode as Decode exposing (Decoder)
import Pages.StaticHttp as StaticHttp


type alias Pokemon =
    { name : String
    , sprite : String
    }


pokemonDetailRequest : StaticHttp.Request (List Pokemon)
pokemonDetailRequest =
    StaticHttp.get
        (Secrets.succeed "https://pokeapi.co/api/v2/pokemon/?limit=3")
        (Decode.field "results"
            (Decode.list
                (Decode.map2 Tuple.pair
                    (Decode.field "name" Decode.string)
                    (Decode.field "url" Decode.string)
                    |> Decode.map
                        (\( name, url ) ->
                            StaticHttp.get (Secrets.succeed url)
                                (Decode.at
                                    [ "sprites", "front_default" ]
                                    Decode.string
                                    |> Decode.map (Pokemon name)
                                )
                        )
                )
            )
        )
        |> StaticHttp.andThen StaticHttp.combine
map2 : (a -> b -> c) -> Request a -> Request b -> Request c

Like map, but it takes in two Requests.

view siteMetadata page =
    StaticHttp.map2
        (\elmPagesStars elmMarkdownStars ->
            { view =
                \model viewForPage ->
                    { title = "Repo Stargazers"
                    , body = starsView elmPagesStars elmMarkdownStars
                    }
            , head = head elmPagesStars elmMarkdownStars
            }
        )
        (get "https://api.github.com/repos/dillonkearns/elm-pages"
            (Decode.field "stargazers_count" Decode.int)
        )
        (get "https://api.github.com/repos/dillonkearns/elm-markdown"
            (Decode.field "stargazers_count" Decode.int)
        )
(value1 -> value2 -> value3 -> valueCombined)
-> Request value1
-> Request value2
-> Request value3
-> Request valueCombined
(value1 -> value2 -> value3 -> value4 -> valueCombined)
-> Request value1
-> Request value2
-> Request value3
-> Request value4
-> Request valueCombined
(value1 -> value2 -> value3 -> value4 -> value5 -> valueCombined)
-> Request value1
-> Request value2
-> Request value3
-> Request value4
-> Request value5
-> Request valueCombined
(value1 -> value2 -> value3 -> value4 -> value5 -> value6 -> valueCombined)
-> Request value1
-> Request value2
-> Request value3
-> Request value4
-> Request value5
-> Request value6
-> Request valueCombined
( value1
-> value2
-> value3
-> value4
-> value5
-> value6
-> value7
-> valueCombined
)
-> Request value1
-> Request value2
-> Request value3
-> Request value4
-> Request value5
-> Request value6
-> Request value7
-> Request valueCombined
( value1
-> value2
-> value3
-> value4
-> value5
-> value6
-> value7
-> value8
-> valueCombined
)
-> Request value1
-> Request value2
-> Request value3
-> Request value4
-> Request value5
-> Request value6
-> Request value7
-> Request value8
-> Request valueCombined
( value1
-> value2
-> value3
-> value4
-> value5
-> value6
-> value7
-> value8
-> value9
-> valueCombined
)
-> Request value1
-> Request value2
-> Request value3
-> Request value4
-> Request value5
-> Request value6
-> Request value7
-> Request value8
-> Request value9
-> Request valueCombined