# Migrating to elm/http 2.0

**URL:** <https://discourse.elm-lang.org/t/migrating-to-elm-http-2-0/3273>\
**Category:** Learn\
**Created:** [March 5, 2019, 8:39pm UTC](https://discourse.elm-lang.org/t/migrating-to-elm-http-2-0/3273 "2019-03-05T20:39:11Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Hermanverschooten](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/hermanverschooten/32/1883_2.png) [@Hermanverschooten](https://discourse.elm-lang.org/u/Hermanverschooten)\
**Post date:** [March 5, 2019, 8:39pm UTC](https://discourse.elm-lang.org/t/migrating-to-elm-http-2-0/3273/1 "2019-03-05T20:39:11Z")

</div>

As many of you probably do, I wanted to upgrade my Elm application to use the new elm/http 2.0 packages. So today I bit the bullet, and did just that. I thought it might be interesting for others, so here we go.

I used elm/http 1.0, NoRedInk/elm-rails and lukewestby/elm-http-builder throughout the existing code.  
This was also an opportunity to streamline this 😀.

## elm.json

I moved the existing elm.json out of the way, and proceeded with `elm init` to make a brand new one.  
I did this because changing the file by hand tends to lead to a lot of issues.  
I also remove elm-stuff, just to be sure.  
`elm install elm/http` (yes! version 2.0), and then installed all my other packages.

# Code changes

I used a certain pattern a lot in my code. I would have a function that would return a `Http.Request` that I would `Http.send` in another module. It took me a while to find a way to keep this flexibility.

```auto
load: Int -> Http.Request Order
load id =
  decoder
  |> Http.get ("/orders/" ++ String.fromInt id)

init: Flags -> (Model, Cmd Msg)
init f =
  (
    { order_id = f.id},
     load f.id |> Http.send Loaded
  )

```

In the same file, this is easy and it just becomes:

```auto
load: Int -> Cmd Msg
load id =
  Http.get 
  { url = "/orders/" ++ String.fromInt id)
  , expect= Http.expectJson Loaded decoder
  }

init: Flags -> (Model, Cmd Msg)
init f =
  (
    { order_id = f.id},
    load f.id
  )

```

But when the `load` function is in another module?

```auto
load : Int -> (Result Http.Error Order -> msg) -> Cmd msg
load id toMsg =
  Http.get 
  { url = "/orders/" ++ String.fromInt id)
  , expect= Http.expectJson toMsg decoder
  }

```

and in the main module:

```auto
init: Flags -> (Model, Cmd Msg)
init f =
  (
    { order_id = f.id},
    load f.id Loaded
  )

```

That was not so bad.

The backend we are using in this app is a Rails application. Rails uses certain conventions, one is that for creating records you use `POST`, for updating `PATCH` or `PUT` and `DELETE` for destroying. Instead of figuring out how to do this I started using elm-rails (elm 0.17) and later on elm-http-builder.  
I switched to elm-http-builder to be able to specify the CSRF token in the header. elm-rails requires me to install an extra `npm` package to find the token in the Rails-generated HTML. I prefer to pass it to my app as a flag. But still there were some elm-rails calls all over the place.

Most of the time the changes were in line with the changes above:

```auto
decoder |> Rails.post "url" body |> Http.send Created

```

to

```auto
Http.post
{ url = "url"
, body =body,
, expect = Http.expectJson Created decoder
}

```

This was possible because those were posts where the CSRF verification is disabled. So better ignore those and send the token as seen below.

The places we used elm-http-builder usually looked something like

```auto
HttpBuilder.post "url"
  |> withHeader "X-CSRF-Token" model.csrf
  |> withJsonBody (encode model.form)
  |> withExpect (Http.expectJson decoder)
  |> toRequest
  |> Http.send Created

```

Becomes

```auto
Http.request
{ method = "POST"
, url = "url"
, headers = [Http.header "X-CSRF-Token" model.csrf]
, body = Http.jsonBody (encode model.form)
, expect = Http.expectJson Created decoder
, timeout = Nothing
, tracker = Nothing
}

```

And exactly the same goes for `PUT`, `PATCH` and `DELETE`.

## RemoteData

Kris Jenkins has the formidable package krisajenkins/remotedata, see his talk: [Slaying a UI Antipattern](https://www.youtube.com/watch?time_continue=1&v=NLcRzOyrH08).

My code looked like this:

```auto
get : Cmd Msg
get =
  Decode.list decode
    |> Http.get "/accountabilities.json"
    |> RemoteData.sendRequest
    |> Cmd.map Loaded

```

and now looks like:

```auto
get : Cmd Msg
get =
  Http.get
    { url = "/accountabilities.json"
    , expect = Http.expectJson (RemoteData.fromResult >> Loaded) (Decode.list decode)
    }

```

Remember the functions I used to make that would return a `Http.Request` and now return a `Cmd msg`?

```auto
load id
  |> RemoteData.sendRequest
  |> Cmd.map Loaded

```

now

```auto
load id (RemoteData.fromResult >> Loaded)

```

## Returning an Int

EDIT  
You may skip the next part, as @hector pointed out to me an Int is valid Json…  
So the expect becomes simply `expect = Http.expectJson Created Decode.int`.  
But I’ll leave the code here, maybe somebody will find it interesting  
/EDIT

There was still one snag, some of my actions return an integer value only, not a json, just an integer.

```auto
post : Model -> Cmd Msg
post model =
  let
    decoder : Decode.Decoder Int
    decoder =
      Decode.int
  in
    HttpBuilder.post "/addresses"
      |> withHeader "X-CSRF-Token" model.csrf
      |> withJsonBody (encode model.form)
      |> withExpect (Http.expectJson decoder)
      |> toRequest
      |> Http.send Created

```

now becomes

```auto
post : Model -> Cmd Msg
post model =
    Http.request
      { method = "POST"
      , url = "/addresses"
      , headers = [Http.header "X-CSRF-Token" model.csrf]
      , body = Http.jsonBody (encode model.form)
      , expect = expectInt Created
      , timeout = Nothing
      , tracker = Nothing
      }

```

Wait, where is that `expectInt` coming from?

```auto
expectInt : (Result Http.Error Int -> msg) -> Http.Expect msg
expectInt toMsg =
    Http.expectStringResponse toMsg <|
        \response ->
            case response of
                Http.GoodStatus_ metadata body ->
                    case Json.decodeString Json.int body of
                        Ok value ->
                            Ok value

                        Err err ->
                            Err (Http.BadBody (Json.errorToString err))

                Http.BadStatus_ metadata body ->
                    Err (Http.BadStatus metadata.statusCode)

                Http.BadUrl_ url ->
                    Err (Http.BadUrl url)

                Http.Timeout_ ->
                    Err Http.Timeout

                Http.NetworkError_ ->
                    Err Http.NetworkError

```

I hope this will be useful.

Herman

---

<div class="post-metadata">

**Author:** ![Hector](https://avatars.discourse-cdn.com/v4/letter/h/43a26b/32.png) [@Hector](https://discourse.elm-lang.org/u/Hector)\
**Post date:** [March 6, 2019, 2:34am UTC](https://discourse.elm-lang.org/t/migrating-to-elm-http-2-0/3273/2 "2019-03-06T02:34:44Z")

</div>

> [@Hermanverschooten](#):
>
> There was still one snag, some of my actions return an integer value only, not a json, just an integer.

I’m not completely sure what you are trying to do here, but note that just a number is valid json.

---

<div class="post-metadata">

**Author:** ![Hermanverschooten](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/hermanverschooten/32/1883_2.png) [@Hermanverschooten](https://discourse.elm-lang.org/u/Hermanverschooten)\
**Post date:** [March 6, 2019, 6:13am UTC](https://discourse.elm-lang.org/t/migrating-to-elm-http-2-0/3273/3 "2019-03-06T06:13:32Z")

</div>

OMG, you are complete right! It is not needed at all.  
I adapted my text, thanks.

---

<div class="post-metadata">

**Author:** ![stevensonmt](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/stevensonmt/32/889_2.png) [@stevensonmt](https://discourse.elm-lang.org/u/stevensonmt)\
**Post date:** [March 9, 2019, 5:59pm UTC](https://discourse.elm-lang.org/t/migrating-to-elm-http-2-0/3273/4 "2019-03-09T17:59:08Z")

</div>

Thank you for this post, it was helpful in figuring out how to upgrade to http 2.0. I must say that deleting elm.json and rebuilding it from scratch is a pain when you have even a handful of dependencies and all you need is to update one of them.

---

<div class="post-metadata">

**Author:** ![stevensonmt](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/stevensonmt/32/889_2.png) [@stevensonmt](https://discourse.elm-lang.org/u/stevensonmt)\
**Post date:** [March 11, 2019, 4:53am UTC](https://discourse.elm-lang.org/t/migrating-to-elm-http-2-0/3273/5 "2019-03-11T04:53:10Z")

</div>

I made a simple CLI tool in rust to take care of this process in a single command. It’s not terribly sophisticated right now, but for anyone interested:

> **[stevensonmt/elmup](https://github.com/stevensonmt/elmup)**
>
> Very simple CLI tool for updating dependencies in your elm app. No safety guarantees but a backup is made of your elm.json so reversion should be simple. - stevensonmt/elmup

  
I’d appreciate any and all feedback.

---

<div class="post-metadata">

**Author:** ![francescortiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/francescortiz/32/1721_2.png) [@francescortiz](https://discourse.elm-lang.org/u/francescortiz)\
**Post date:** [March 15, 2019, 9:47am UTC](https://discourse.elm-lang.org/t/migrating-to-elm-http-2-0/3273/6 "2019-03-15T09:47:14Z")

</div>

Great tool! I will definitively use at some point to migrate old projects. Thank you!

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [March 25, 2019, 9:51am UTC](https://discourse.elm-lang.org/t/migrating-to-elm-http-2-0/3273/7 "2019-03-25T09:51:50Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
