Create a GET
request.
import Http
type Msg
= GotText (Result Http.Error String)
getPublicOpinion : Cmd Msg
getPublicOpinion =
Http.get
{ url = "https://elm-lang.org/assets/public-opinion.txt"
, expect = Http.expectString GotText
}
You can use functions like [expectString
]{@link module:Http.expectString} and
[expectJson
]{@link module:Http.expectJson} to interpret the response in different ways. In
this example, we are expecting the response body to be a String
containing
the full text of Public Opinion by Walter Lippmann.
Note: Use elm/url
to build reliable URLs.
Create a POST
request. So imagine we want to send a POST request for
some JSON data. It might look like this:
import Http
import Json.Decode exposing (list, string)
type Msg
= GotBooks (Result Http.Error (List String))
postBooks : Cmd Msg
postBooks =
Http.post
{ url = "https://example.com/books"
, body = Http.emptyBody
, expect = Http.expectJson GotBooks (list string)
}
Notice that we are using expectJson
to interpret the response
as JSON. You can learn more about how JSON decoders work [here][] in the guide.
We did not put anything in the body of our request, but you can use functions
like stringBody
and jsonBody
if you need to
send information to the server.
[here]: https://guide.elm-lang.org/interop/json.html
Create a custom request. For example, a PUT for files might look like this:
import File
import Http
type Msg = Uploaded (Result Http.Error ())
upload : File.File -> Cmd Msg
upload file =
Http.request
{ method = "PUT"
, headers = []
, url = "https://example.com/publish"
, body = Http.fileBody file
, expect = Http.expectWhatever Uploaded
, timeout = Nothing
, tracker = Nothing
}
It lets you set custom headers
as needed. The timeout
is the number of
milliseconds you are willing to wait before giving up. The tracker
lets you
cancel
and track
requests.
An HTTP header for configuring requests. See a bunch of common headers here.
Create a Header
.
header "If-Modified-Since" "Sat 29 Oct 1994 19:43:31 GMT"
header "Max-Forwards" "10"
header "X-Requested-With" "XMLHttpRequest"
Represents the body of a Request
.
Put some Bytes
in the body of your Request
. This allows you to use
elm/bytes
to have full control over the binary
representation of the data you are sending. For example, you could create an
archive.zip
file and send it along like this:
import Bytes exposing (Bytes)
zipBody : Bytes -> Body
zipBody bytes =
bytesBody "application/zip" bytes
The first argument is a MIME type
of the body. In other scenarios you may want to use MIME types like image/png
or image/jpeg
instead.
Note: Use track
to track upload progress.
Create an empty body for your Request
. This is useful for GET requests
and POST requests where you are not sending any data.
Put some JSON value in the body of your Request
. This will automatically
add the Content-Type: application/json
header.
Put some string in the body of your Request
. Defining jsonBody
looks
like this:
import Json.Encode as Encode
jsonBody : Encode.Value -> Body
jsonBody value =
stringBody "application/json" (Encode.encode 0 value)
The first argument is a MIME type of the body. Some servers are strict about this!
A Request
can fail in a couple ways:
BadUrl
means you did not provide a valid URL.Timeout
means it took too long to get a response.NetworkError
means the user turned off their wifi, went in a cave, etc.BadStatus
means you got a response back, but the status code indicates failure.BadBody
means you got a response back with a nice status code, but the body
of the response was something unexpected. The String
in this case is a
debugging message that explains what went wrong with your JSON decoder or
whatever.expectStringResponse
and
expectBytesResponse
to get more flexibility on this.Logic for interpreting a response body.
A type representing Json
Expect valid json but do not care about the contents. Probably not a good idea as skipping checking the contents may cause hard to debug runtime exceptions further ahead.
Expect the response body to be binary data. For example, maybe you are talking to an endpoint that gives back ProtoBuf data:
import Bytes.Decode as Bytes
import Http
type Msg
= GotData (Result Http.Error Data)
getData : Cmd Msg
getData =
Http.get
{ url = "/data"
, expect = Http.expectBytes GotData dataDecoder
}
-- dataDecoder : Bytes.Decoder Data
You would use elm/bytes
to decode the binary
data according to a proto definition file like example.proto
.
If the decoder fails, you get a BadBody
error that just indicates that
something went wrong. It probably makes sense to debug by peeking at the
bytes you are getting in the browser developer tools or something.
Expect the response body to be JSON. Like if you want to get a random cat GIF you might say:
import Http
import Json.Decode exposing (Decoder, field, string)
type Msg
= GotGif (Result Http.Error String)
getRandomCatGif : Cmd Msg
getRandomCatGif =
Http.get
{ url = "https://api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=cat"
, expect = Http.expectJson GotGif gifDecoder
}
gifDecoder : Decoder String
gifDecoder =
field "data" (field "image_url" string)
The official guide goes through this particular example [here][]. That page
also introduces [elm/json
][json] to help you get started turning JSON into
Elm values in other situations.
[here]: https://guide.elm-lang.org/interop/json.html
[json]: /packages/elm/json/latest/
If the JSON decoder fails, you get a BadBody
error that tries to explain
what went wrong.
Expect the response body to be a String
. Like when getting the full text
of a book:
import Http
type Msg
= GotText (Result Http.Error String)
getPublicOpinion : Cmd Msg
getPublicOpinion =
Http.get
{ url = "https://elm-lang.org/assets/public-opinion.txt"
, expect = Http.expectString GotText
}
The response body is always some sequence of bytes, but in this case, we
expect it to be UTF-8 encoded text that can be turned into a String
.
Expect the response body to be whatever. It does not matter. Ignore it! For example, you might want this when uploading files:
import Http
type Msg
= Uploaded (Result Http.Error ())
upload : File -> Cmd Msg
upload file =
Http.post
{ url = "/upload"
, body = Http.fileBody file
, expect = Http.expectWhatever Uploaded
}
The server may be giving back a response body, but we do not care about it.
Turn Receiving
progress into a useful fraction for progress bars.
fractionReceived { received = 0, size = Just 1024 } == 0.0
fractionReceived { received = 256, size = Just 1024 } == 0.25
fractionReceived { received = 512, size = Just 1024 } == 0.5
-- fractionReceived { received = 0, size = Nothing } == 0.0
-- fractionReceived { received = 256, size = Nothing } == 0.0
-- fractionReceived { received = 512, size = Nothing } == 0.0
The size
here is based on the [Content-Length
][cl] header which may be
missing in some cases. A server may be misconfigured or it may be streaming
data and not actually know the final size. Whatever the case, this function
will always give 0.0
when the final size is unknown.
Furthermore, the Content-Length
header may be incorrect! The implementation
clamps the fraction between 0.0
and 1.0
, so you will just get 1.0
if
you ever receive more bytes than promised.
Note: If you are streaming something, you can write a custom version of
this function that just tracks bytes received. Maybe you show that 22kb or 83kb
have been downloaded, without a specific fraction. If you do this, be wary of
divide-by-zero errors because size
can always be zero!
[cl]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length
Turn Sending
progress into a useful fraction.
fractionSent { sent = 0, size = 1024 } == 0.0
fractionSent { sent = 256, size = 1024 } == 0.25
fractionSent { sent = 512, size = 1024 } == 0.5
-- fractionSent { sent = 0, size = 0 } == 1.0
The result is always between 0.0
and 1.0
, ensuring that any progress bar
animations never go out of bounds.
And notice that size
can be zero. That means you are sending a request with
an empty body. Very common! When size
is zero, the result is always 1.0
.
Note: If you create your own function to compute this fraction, watch out
for divide-by-zero errors!
Track the progress of a request. Create a request
where
tracker = Just "form.pdf"
and you can track it with a subscription like
track "form.pdf" GotProgress
.
Try to cancel an ongoing request based on a tracker
.
Expect a Response
with a Bytes
body.
It works just like expectStringResponse
, giving you
more access to headers and more leeway in defining your own errors.
Expect a Response
with a String
body. So you could define
your own expectJson
like this:
import Http
import Json.Decode as D
expectJson : (Result Http.Error a -> msg) -> D.Decoder a -> Expect msg
expectJson toMsg decoder =
expectStringResponse toMsg <|
\response ->
case response of
Http.BadUrl_ url ->
Err (Http.BadUrl url)
Http.Timeout_ ->
Err Http.Timeout
Http.NetworkError_ ->
Err Http.NetworkError
Http.BadStatus_ metadata body ->
Err (Http.BadStatus metadata.statusCode)
Http.GoodStatus_ metadata body ->
case D.decodeString decoder body of
Ok value ->
Ok value
Err err ->
BadBody (D.errorToString err)
This function is great for fancier error handling and getting response headers.
For example, maybe when your sever gives a 404 status code (not found) it also
provides a helpful JSON message in the response body. This function lets you
add logic to the BadStatus_
branch so you can parse that JSON and give users
a more helpful message! Or make your own custom error type for your particular
application!
Generated using TypeDoc
Creates the effect manager.