Downloading file from POST request with Elm

Viewed 952

I'm in the position that I have an HTTP POST endpoint /render that returns a PDF document, and would like to present a button/link to the user that will cause this document to be downloaded and saved to a file without navigating away from my Elm app.

Ideally the POST will accept a text/plain body with a custom format, but I could rework the endpoint to accept multipart/form-data or application/x-www-form-urlencoded.

I can download the raw data to the Elm app successfully as follows, but I'm at a loss for how to save the file to disk.

import Http

render : String -> Http.Request String
render body =
  Http.request
    { method = "POST"
    , headers = []
    , url = "/render"
    , body = Http.stringBody "text/plain" body
    , expect = expectString
    , timeout = Nothing
    , withCredentials = False
    }
1 Answers

I did it using expectBytes rather then expectString so my code is

import Bytes exposing (Bytes)
import File.Download as Download

Msg = .. | FormUploaded (Result Http.Error Bytes)

Http.post
        { url = "/passports"
        , body =
            Http.multipartBody...
        , expect = Http.expectBytesResponse FormUploaded (resolve Ok)
        }


downloadPdf : Bytes -> Cmd msg
downloadPdf pdfContent =
    Download.bytes "form.pdf" "application/pdf" pdfContent

update : Msg -> Model -> ( Model, Cmd Msg )
update model =
...
    FormUploaded (Ok response) ->
            ( model, downloadPdf response )
    FormUploaded (Err err) ->
            ( model, Cmd.none )

-- this helper function copied from https://github.com/elm/http/blob/2.0.0/src/Http.elm#L514-L521
resolve : (body -> Result String a) -> Http.Response body -> Result Http.Error a
resolve toResult response =
    case response of
        BadUrl_ url ->
            Err (BadUrl url)

        Timeout_ ->
            Err Timeout

        NetworkError_ ->
            Err NetworkError

        BadStatus_ metadata _ ->
            Err (BadStatus metadata.statusCode)

        GoodStatus_ _ body ->
            Result.mapError BadBody (toResult body)

it's not ideal but works

PS: I got help from Elm Slack Channel https://elmlang.slack.com/

Related