How to read cookies in Elm?

Viewed 270

I've learned in this SO question that there currently is no simple way to turn cookie-based CSRF tokens into HTTP request headers in Elm. Thus, to write a single page application (SPA) that works nicely with a Django Rest Framework backend, I need to manually retrieve the CSRF-Token from the corresponding cookie value.

How do I retrieve a cookie value in Elm? Does Elm provide runtime support for this via some Command? Or do I need to retrieve the cookie using plain JavaScript and provide it to the ELM SPA via a port?

2 Answers

As of Elm 0.9, you need to use Ports to read the cookie from JavaScript and pass it back to the Elm application.

In my application, I do the following. I define a fetchCsrfToken port that I use from Elm to call a JavaScript function that reads the cookie. That function then triggers a callback to Elm via a csrfTokenReciever port. My Elm application subscribes to that event via subscriptions.

-- Ports.elm

port fetchCsrfToken : () -> Cmd msg
port csrfTokenReciever : (String -> msg) -> Sub msg
-- Main.elm

init : Flags -> Url -> Key -> ( Model, Cmd Msg )
init flags url key =
  -- ...
  (model, Ports.fetchCsrfToken ())
  

subscriptions : Model -> Sub Msg
subscriptions model =
  Ports.csrfTokenReciever GotCsrfToken
// index.js

app.ports.fetchCsrfToken.subscribe(function (str) {
  const value = getCookie('csrftoken')
  if (value === null) {
    app.ports.csrfTokenReciever.send('')
  } else {
    app.ports.csrfTokenReciever.send(value)
  }
})

Using Elm 0.19.1

First solution:

Use of 2 ports, a subscription and some JS/TS code like @viam0Zah mentioned.


Second solution:

Pass the CSRF into your flags at init

    const app = Elm.Main.init({
      node: document.querySelector("main"),
      flags: {
        csrfToken: getCookie('csrftoken')
      }
    });

add csrfToken to Flags

type alias Flags =
    { ---
    , csrfToken : String
    }

And don't forget to add a decoder for the csrfToken:

import Json.Decode as D

flagsDecoder : D.Decoder Flags
flagsDecoder =
    D.succeed Flags
        |> ---
        |> D.required "csrfToken" D.string

If you want to be more robust and extend type safety for both solutions - flags and ports, you should check out https://elm-ts-interop.com/, it's just amazing!

Related