What we’d prefer instead is a function that has a type signature like this:expectStringDetailed : (Result Error ( Metadata, String ) -> msg) -> Expect msgInstead of returning only the body as a String, return both the metadata and the body as ( Metadata, String ).
We can implement this using expectStringResponse.
Let’s take a look at some more type signatures from elm/http to better understand how to use expectStringResponse.
expectStringResponse allows us to send an HTTP request and return any Result (Result x a) we want.
We just have to provide a function that converts a Response and into our desired Result.
The Result we’d like for now is (Result Error ( Metadata, String ).
Let’s create a function that converts a Response into our desired Result, and call it convertResponseString.
On a successful HTTP request, return ( Metadata, String ) instead of just String!In the case of a successful HTTP request, return Ok ( metadata, body ) instead of only the body!.Now that we have the Result we want, pass this function to expectStringResponse to implement expectStringDetailed.
Easy!.Using this function, our HTTP requests return the response body as well as the metadata!.We can access the headers and response URL from the metadata.
That is, if our request was successful… what if it fails?Creating a Custom Error TypeIf the HTTP request fails, expectString will return data of the type Error from elm/http.
Compare this with Response (shown earlier) and you’ll find that they’re very similar…but with some minor yet impactful differences.
Notice that theBadStatus_ case of Response includes both the metadata and the body.
That’s because if the HTTP request fails with a bad status code, it still contains a response body!However, the BadStatus case of Error only contains an Int, which is the status code of the response.
The body and the rest of the metadata is discarded by elm/http!To fix this, we can create our own custom error type that includes the entire metadata along with the body in the BadStatus case.
Let’s change expectStringDetailed’s type signature to use our custom error, DetailedError instead of Http.
Error!expectStringDetailed: (Result DetailedError ( Metadata, String ) -> msg) -> Expect msgAll that’s left to do is modify convertResponseString to return our custom error.
In the BadStatus branch, we put in the complete metadata and body instead of just the status code.
Sweet!.We’ve now implemented a function, expectStringDetailed, that will include all the detailed information that we might find useful in the response, like the metadata and the error body, whether or not our HTTP request was successful!Full Examples and a Dedicated PackageI’ve created an Ellie that shows a complete example on how to use the expectStringDetailed function we created, from sending the request using Http.
get to displaying the detailed response in our page.
A full end-to-end example showing how to use expectStringDetailed.
I’ve also implemented the equivalent functions expectJsonDetailed and expectBytesDetailed.
I had to make the following modifications:The respective convert functions take in either a JSON or Bytes decoder as the first argument.
The metadata and body are included in the BadBody branch of the ErrorDetailed type.
This branch is entered when we try and decode JSON or Bytes and fail!The ErrorDetailed type is now polymorphic, as the body could be either String or Bytes depending on which expect function is used.
Full example including implementation of expectJsonDetailed and expectBytesDetailedFinally, I have published a package that contains all of these functions, so you don’t have to implement them yourself every time.
In addition to the functions discussed, you can also get the detailed response as a record, mock API responses, and more!.Here’s an Ellie that does the exact thing as the previous example, but using my package:Full example showing how to handle detailed responses using jzxhuang/http-extras.
ConclusionMore often than not, we need to go beyond 200 OK.
We require more than just the response body on a successful request and the status code on a failed request.
In Elm, getting these details it isn’t so straightforward — you need to use the ‘elaborate’ functions expectStringResponse and expectBytesResponse to return a more detailed Result.
It’s a bit tedious and unintuitive to do this all the time, which leads me to wonder if it was worth simplifying the expect functions at the cost of this extra hassle.
As I mentioned at the beginning, many people are running into the same problem!.It’s definitely an interesting point of discussion…More often than not, we need to go beyond 200 OK.
Hopefully, this guide helped you understand how to handle HTTP responses in detail with elm/http.
Again, my package jzxhuang/http-extras includes everything in this guide and more.
Other great packages for working with HTTP include jinjor/elm-req (focuses on tasks) and rakutentech/http-trinity (includes an interesting way of mocking requests, which I also contributed to).
Thanks for reading, and if you have any questions or comments, post away!.. More details