Elm Language Backend Integration Basic elm Http.post json request to node.js express server


Example

Live upcase server that returns error when input string is longer than 10 characters.

Server:

const express = require('express'),
    jsonParser = require('body-parser').json(),
    app = express();

// Add headers to work with elm-reactor
app.use((req, res, next) => {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000');
    res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
    res.setHeader('Access-Control-Allow-Credentials', true);
    next();
});

app.post('/upcase', jsonParser, (req, res, next) => {
    // Just an example of possible invalid data for an error message demo
    if (req.body.input && req.body.input.length < 10) {
        res.json({
            output: req.body.input.toUpperCase()
        });
    } else {
        res.status(500).json({
            error: `Bad input: '${req.body.input}'`
        });
    }
});

const server = app.listen(4000, () => {
    console.log('Server is listening at http://localhost:4000/upcase');
});

Client:

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Http
import Json.Decode as JD
import Json.Encode as JE

main : Program Never Model Msg
main =
    Html.program
        { init = init
        , view = view
        , update = update
        , subscriptions = subscriptions
        }

-- MODEL

type alias Model =
    { output: String
    , error: Maybe String
    }

init : (Model, Cmd Msg)
init =
    ( Model "" Nothing
    , Cmd.none
    )

-- UPDATE

type Msg
    = UpcaseRequest ( Result Http.Error String )
    | InputString String

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        UpcaseRequest (Ok response) ->
            ( { model | output = response, error = Nothing }, Cmd.none )

        UpcaseRequest (Err err) ->
            let
                errMsg = case err of
                    Http.Timeout ->
                        "Request timeout"

                    Http.NetworkError ->
                        "Network error"

                    Http.BadPayload msg _ ->
                        msg

                    Http.BadStatus response ->
                        case JD.decodeString upcaseErrorDecoder response.body of
                            Ok errStr ->
                                errStr

                            Err _ ->
                                response.status.message

                    Http.BadUrl msg ->
                        "Bad url: " ++ msg
            in
                ( { model | output = "", error = Just errMsg }, Cmd.none )

        InputString str ->
            ( model, upcaseRequest str )

-- VIEW

view : Model -> Html Msg
view model =
    let
        outDiv = case model.error of
            Nothing ->
                div []
                    [ label [ for "outputUpcase" ] [ text "Output" ]
                    , input [ type_ "text", id "outputUpcase", readonly True, value model.output ] []
                    ]

            Just err ->
                div []
                    [ label [ for "errorUpcase" ] [ text "Error" ]
                    , input [ type_ "text", id "errorUpcase", readonly True, value err ] []
                    ]
    in
        div []
            [ div []
                [ label [ for "inputToUpcase" ] [ text "Input" ]
                , input [ type_ "text", id "inputToUpcase", onInput InputString ] []
                ]
            , outDiv
            ]

-- SUBSCRIPTIONS

subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.none

-- HELPERS

upcaseSuccessDecoder : JD.Decoder String
upcaseSuccessDecoder = JD.field "output" JD.string

upcaseErrorDecoder : JD.Decoder String
upcaseErrorDecoder = JD.field "error" JD.string

upcaseRequestEncoder : String -> JE.Value
upcaseRequestEncoder str = JE.object [ ( "input", JE.string str ) ]

upcaseRequest : String -> Cmd Msg
upcaseRequest str =
    let
        req = Http.post "http://localhost:4000/upcase" ( Http.jsonBody <| upcaseRequestEncoder str ) upcaseSuccessDecoder
    in
        Http.send UpcaseRequest req