Create HTTP requests that return more detailed responses.
The metadata and original body of an HTTP response are often very useful.
Maybe your server returns a useful error message you'd like to try and decode,
or you want to access the header of a successful response.
Unfortunately, this information is discarded in the responses in elm/http.
This module lets you create HTTP requests that keep that useful information around.
The API is designed so that usage of this module is exactly the same as using elm/http,
with the only difference being that a more detailed Result is returned.
Create an HTTP request like you normally would -
just use this module's expect functions instead of the ones from the default package.
import Http
import Http.Detailed
type Msg
= GotText (Result (Http.Detailed.Error String) ( String, Http.Metadata ))
| ...
Http.get
{ url = "https://elm-lang.org/assets/public-opinion.txt"
, expect = Http.Detailed.expectString GotText
}If a successful response is received, a Tuple containing the expected body and the metadata is returned.
You can access a header from the metadata if needed.
In case of an error, a custom Error type is returned which keeps the metadata and body around if applicable,
rather than discarding them. Maybe you want to try and decode the error message!
Your update function might look a bit like this:
update msg model =
case msg of
GotText detailedResponse ->
case detailedResponse of
Ok (text, metadata ) ->
-- Do something with the metadata if you need!
Err error ->
case error of
Http.Detailed.BadStatus metadata body statusCode ->
-- Try to decode the body - it might be a useful error message!
Http.Detailed.Timeout ->
-- No metadata is given here - the request timed out
...
...Exactly like the expect functions from elm/http - usage of the API is the same.
The difference is that the Result is more detailed.
Tuple containing the expected body and the metadata.Error type which keeps the metadata and body around if applicable.A modified version of the examples from elm/http are included for each function to help guide you in using this module.
Expect the response body to be a String.
import Http
import Http.Detailed
type Msg
= GotText (Result (Http.Detailed.Error String) ( String, Http.Metadata ))
getPublicOpinion : Cmd Msg
getPublicOpinion =
Http.get
{ url = "https://elm-lang.org/assets/public-opinion.txt"
, expect = Http.Detailed.expectString GotText
}On success, return a Tuple containing the body as a String and the metadata. On error, return our custom Error type.
When using this, the error will never be of type BadBody.
Expect the response body to be JSON, and try to decode it.
import Http
import Http.Detailed
import Json.Decode exposing (Decoder, field, string)
type Msg
= GotGif (Result (Http.Detailed.Error String) ( String, Http.Metadata ))
getRandomCatGif : Cmd Msg
getRandomCatGif =
Http.get
{ url = "https://api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=cat"
, expect = Http.Detailed.expectJson GotGif gifDecoder
}
gifDecoder : Decoder String
gifDecoder =
field "data" (field "image_url" string)On success, return a Tuple containing the decoded body and the metadata. On error, return our custom Error type.
If the JSON decoder fails, you get a BadBody error that tries to explain what went wrong.
Expect the response body to be binary data, and try to decode it.
import Bytes exposing (Bytes)
import Bytes.Decode
import Http
import Http.Detailed
type Msg
= GotData (Result (Http.Detailed.Error Bytes) ( Data, Http.Metadata ))
getData : Cmd Msg
getData =
Http.get
{ url = "/data"
, expect = Http.Detailed.expectBytes GotData dataDecoder
}
dataDecoder : Bytes.Decode.Decoder Data
-- This is a Bytes decoder ...On success, return the a tuple containing the decoded body and the metadata. On error, return our custom Error type.
If the Bytes decoder fails, you get a BadBody error that just indicates that
something went wrong. You can try to debug by taking a look at the
bytes you are getting in the browser DevTools or something.
Expect the response body to be whatever. It does not matter. Ignore it!
import Http
import Http.Extras
type Msg
= Uploaded (Result (Http.Extras.Error Bytes) ())
upload : File -> Cmd Msg
upload file =
Http.post
{ url = "/upload"
, body = Http.fileBody file
, expect = Http.Extras.expectWhatever Uploaded
}The server may be giving back a response body, but we do not care about it.
On error, return our custom Error type. It will never be BadBody.
These functions transform an Http.Response value into the detailed Result that is returned in each expect function in this module.
You can use these to build your own expect functions.
For example, to create Http.Detailed.expectJson:
import Http
import Http.Detailed
import json.Decode
expectJson : (Result (Http.Detailed.Error String) ( a, Http.Metadata ) -> msg) -> Json.Decode.Decoder a -> Http.Expect msg
expectJson toMsg decoder =
Http.expectStringResponse toMsg (responseToJson decoder)Use this with Mock to mock a request with a detailed response!
import Http
import Http.Detailed
import Http.Mock
type Msg
= GotText (Result (Http.Detailed.Error String) ( String, Http.Metadata ))
| ...
-- Mock a request, with a Detailed Result!
Http.get
{ url = "https://fakeurl.com"
, expect = Http.Mock.expectStringResponse Http.Timeout_ GotText Http.Detailed.responseToString
}
Our custom error type. Similar to
Http.Error, but keeps the metadata and body around inBadStatusandBadBodyrather than discarding them. Maybe your API gives a useful error message you want to decode!bodycan be eitherStringorBytes, depending on whichexpectfunction you use.expectJsonandexpectStringwill return aStringbodyexpectWhateverandexpectByteswill return aBytesbody.The
BadBodystate will only be entered when usingexpectJsonorexpectBytes.