BackendTask.Http
requests are an alternative to doing Elm HTTP requests the traditional way using the elm/http
package.
The key differences are:
BackendTask.Http.Request
s are performed once at build time (Http.Request
s are performed at runtime, at whenever point you perform them)BackendTask.Http.Request
s have a built-in BackendTask.andThen
that allows you to perform follow-up requests without using tasksIf 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 BackendTask.Http.Request
s as well as the simplicity and robustness that comes with it. Read more about these benefits
in this article introducing BackendTask.Http requests and some concepts around it.
get : String -> Expect a -> BackendTask { fatal : FatalError, recoverable : Error } a
A simplified helper around BackendTask.Http.getWithOptions
, which builds up a GET request with
the default retries, timeout, and HTTP caching options. If you need to configure those options or include HTTP request headers,
use the more flexible getWithOptions
.
import BackendTask
import BackendTask.Http
import FatalError exposing (FatalError)
getRequest : BackendTask (FatalError Error) String
getRequest =
BackendTask.Http.get
"https://api.github.com/repos/dillonkearns/elm-pages"
BackendTask.Http.expectString
getJson : String -> Json.Decode.Decoder a -> BackendTask { fatal : FatalError, recoverable : Error } a
A simplified helper around BackendTask.Http.get
, which builds up a BackendTask.Http GET request with expectJson
.
import BackendTask
import BackendTask.Http
import FatalError exposing (FatalError)
import Json.Decode as Decode exposing (Decoder)
getRequest : BackendTask (FatalError Error) Int
getRequest =
BackendTask.Http.getJson
"https://api.github.com/repos/dillonkearns/elm-pages"
(Decode.field "stargazers_count" Decode.int)
post : String -> Body -> Expect a -> BackendTask { fatal : FatalError, recoverable : Error } a
Analogous to the Expect
type in the elm/http
package. This represents how you will process the data that comes
back in your BackendTask.Http request.
You can derive ExpectJson
from ExpectString
. Or you could build your own helper to process the String
as XML, for example, or give an elm-pages
build error if the response can't be parsed as XML.
expectString : Expect String
Gives the HTTP response body as a raw String.
import BackendTask exposing (BackendTask)
import BackendTask.Http
request : BackendTask String
request =
BackendTask.Http.request
{ url = "https://example.com/file.txt"
, method = "GET"
, headers = []
, body = BackendTask.Http.emptyBody
}
BackendTask.Http.expectString
expectJson : Json.Decode.Decoder value -> Expect value
Handle the incoming response as JSON and don't optimize the asset and strip out unused values.
Be sure to use the BackendTask.Http.request
function if you want an optimized request that
strips out unused JSON to optimize your asset size. This function makes sense to use for things like a GraphQL request
where the JSON payload is already trimmed down to the data you explicitly requested.
If the function you pass to expectString
yields an Err
, then you will get a build error that will
fail your elm-pages
build and print out the String from the Err
.
expectBytes : Bytes.Decode.Decoder value -> Expect value
expectWhatever : value -> Expect value
request : { url : String, method : String, headers : List ( String, String ), body : Body, retries : Maybe Basics.Int, timeoutInMs : Maybe Basics.Int } -> Expect a -> BackendTask { fatal : FatalError, recoverable : Error } a
The way you build a body is analogous 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!
Pages.Internal.StaticHttpBody.Body
A body for a BackendTask.Http request.
emptyBody : Body
Build an empty body for a BackendTask.Http request. See elm/http's Http.emptyBody
.
stringBody : String -> String -> Body
Builds a string body for a BackendTask.Http request. See elm/http's Http.stringBody
.
Note from the elm/http
docs:
The first argument is a MIME type of the body. Some servers are strict about this!
jsonBody : Json.Encode.Value -> Body
Builds a JSON body for a BackendTask.Http request. See elm/http's Http.jsonBody
.
bytesBody : String -> Bytes -> Body
Build a body from Bytes
for a BackendTask.Http request. See elm/http's Http.bytesBody
.
elm-pages
performs GET requests using a local HTTP cache by default. These requests are not performed using Elm's elm/http
,
but rather are performed in NodeJS. Under the hood it uses the NPM package make-fetch-happen
.
Only GET requests made with get
, getJson
, or getWithOptions
use local caching. Requests made with BackendTask.Http.request
are not cached, even if the method is set to GET
.
In dev mode, assets are cached more aggressively by default, whereas for a production build assets use a default to revalidate each cached response's freshness before using it (the ForceRevalidate
CacheStrategy
).
The default caching behavior for GET requests is to use a local cache in .elm-pages/http-cache
. This uses the same caching behavior
that browsers use to avoid re-downloading content when it hasn't changed. Servers can set HTTP response headers to explicitly control
this caching behavior.
cache-control
HTTP response headers let you set a length of time before considering an asset stale. This could mean that the server considers it acceptable for an asset to be somewhat outdated, or this could mean that the asset is guaranteed to be up-to-date until it is stale - those semantics are up to the server.Last-Modified
and ETag
HTTP response headers can be returned by the server allow Conditional Requests. Conditional Requests let us send back the Last-Modified
timestamp or etag
hash for assets that are in our local cache to the server to check if the asset is fresh, and skip re-downloading it if it is unchanged (or download a fresh one otherwise).It's important to note that depending on how the server sets these HTTP response headers, we may have outdated data - either because the server explicitly allows assets to become outdated with their cache-control headers, OR because cache-control headers are not set. When these headers aren't explicitly set, clients are allowed to cache assets for 10% of the amount of time since it was last modified. For production builds, the default caching will ignore both the implicit and explicit information about an asset's freshness and always revalidate it before using a locally cached response.
getWithOptions : { url : String, expect : Expect a, headers : List ( String, String ), cacheStrategy : Maybe CacheStrategy, retries : Maybe Basics.Int, timeoutInMs : Maybe Basics.Int, cachePath : Maybe String } -> BackendTask { fatal : FatalError, recoverable : Error } a
Perform a GET request, with some additional options for the HTTP request, including options for caching behavior.
retries
- Default is 0. Will try performing request again if set to a number greater than 0.timeoutInMs
- Default is no timeout.cacheStrategy
- The caching options are passed to the NPM package make-fetch-happen
cachePath
- override the default directory for the local HTTP cache. This can be helpful if you want more granular control to clear some HTTP caches more or less frequently than others. Or you may want to preserve the local cache for some requests in your build server, but not store the cache for other requests.withMetadata : (Metadata -> value -> combined) -> Expect value -> Expect combined
{ url : String
, statusCode : Basics.Int
, statusText : String
, headers : Dict String String
}