Elixir API and Elm SPA - Part 1
Tags: elixir, elm, api, spa
Published: 2018-06-12 22:44:47

---
title: Elixir API and Elm SPA - Part 1
published: true
description: Building a CRUD app with Elixir as the JSON REST API and Elm as the SPA.
tags: elixir, elm, api, spa
canonical_url: https://blog.miguelcoba.com/elixir-api-and-elm-spa-part-1
---

# Part 1: Elixir App creation

I am going to create a demo app with an Elixir API on the backend and a separated Elm SPA on the frontend. The app will be a simple CRUD app with what I have found to be the best practices so far. The app will be a simple market app where users can post announcements for things to sell and other users can see the offers and buy them.
The name of the app will be Toltec.

## Series

1. Part 1 - Elixir App creation
2. [Part 2 - Adds Guardian Authentication](https://dev.to/miguelcoba/elixir-api-and-elm-spa---part-2-1pca)
3. [Part 3: Elm App creation and Routing setup](https://dev.to/miguelcoba/elixir-api-and-elm-spa---part-3-3n0h)
4. [Part 4: Adding Login and Register pages](https://dev.to/miguelcoba/elixir-api-and-elm-spa---part-4-4il0)
5. [Part 5: Persisting session data to localStorage](https://dev.to/miguelcoba/elixir-api-and-elm-spa---part-5-4j1k)

## Assumptions

I assume you are using:

- Elixir 1.6.5
- Erlang 20
- Elm 0.18
- PostgreSQL 10.4

## Create the App

Let's create a basic app for the API. We don't need html or brunch as we don't render html at all. We'll just expose a JSON REST API:

```bash
mix phx.new toltec-api --app toltec --no-brunch --no-html --binary-id
```

## Configure auto formatting

Before adding any code let's configure code auto formatting. Add a file in the root of the project called *.formatter.exs*:

```elixir
# .formatter.exs

[
  inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
```

And the run the format command:

```bash
mix format
```

This should format all the code in your project.

Now you need to configure your editor to automatically run this command when you save a file. For VS Code, for example, you can use the [vscode-elixir](https://marketplace.visualstudio.com/items?itemName=mjmcloug.vscode-elixir) and [vscode-elixir-formatter](https://marketplace.visualstudio.com/items?itemName=sammkj.vscode-elixir-formatter) extensions.

## Create Accounts

We are going to use the [comeonin](https://github.com/riverrun/comeonin) package to handle the password hashing in our app. Add this dependency in mix.exs

```elixir
 # mix.exs

  defp deps do
    [
      {:phoenix, "~> 1.3.0"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_ecto, "~> 3.2"},
      {:postgrex, ">= 0.0.0"},
      {:gettext, "~> 0.11"},
      {:cowboy, "~> 1.0"},
      {:comeonin, "~> 4.0"},
      {:argon2_elixir, "~> 1.2"}
    ]
  end
```

And get the dependencies

```bash
mix deps.get
```

Now let's add the User schema and an Accounts context to hold all this logic

```bash
mix phx.gen.context Accounts User users name:string email:string:unique password_hash:string
```

This will create a user migration, a user schema and an accounts context. For the users I won't use UUID primary keys, so I am going to remove the binary_id config from the users migration, but you're free to keep it if you want.
Change the migration to:

```elixir
# priv/repo/migrations/20180612062911_create_users.exs

  def change do
    execute("CREATE EXTENSION citext;")

    create table(:users) do
      add(:name, :string, null: false)
      add(:email, :citext, null: false)
      add(:password_hash, :string, null: false)

      timestamps()
    end

    create(unique_index(:users, [:email]))
  end
```

And lets add some basic methods to the user schema:

```elixir
 # lib/toltec/accounts/user.ex

 defmodule Toltec.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset
  alias Toltec.Accounts.User

  schema "users" do
    field(:email, :string)
    field(:name, :string)
    field(:password, :string, virtual: true)
    field(:password_hash, :string)

    timestamps()
  end

  def changeset(%User{} = user, attrs) do
    user
    |> cast(attrs, [:name, :email])
    |> validate_required([:name, :email])
    |> validate_length(:name, min: 2, max: 255)
    |> validate_length(:email, min: 5, max: 255)
    |> unique_constraint(:email)
    |> validate_format(:email, ~r/@/)
  end

  def registration_changeset(%User{} = user, attrs) do
    user
    |> changeset(attrs)
    |> cast(attrs, [:password])
    |> validate_required([:password])
    |> validate_length(:password, min: 8, max: 100)
    |> put_password_hash()
  end

  defp put_password_hash(changeset) do
    case changeset do
      %Ecto.Changeset{valid?: true, changes: %{password: password}} ->
        put_change(changeset, :password_hash, Comeonin.Argon2.hashpwsalt(password))

      _ ->
        changeset
    end
  end
end
```
I have added a *password* virtual field, to temporarily hold the clear-text password. This will never be saved to the DB. I also added two changesets, one for changing the password and one for the other fields. The *put_password_hash()* function uses the comeonin library to get a hash of the password and put that on the *password_hash* field that will be stored in DB.

Now modify the Accounts context that holds all the logic for accounts to look like this:

```elixir
# lib/toltec/accounts/accounts.ex

defmodule Toltec.Accounts do
  import Ecto.Query, warn: false
  alias Toltec.Repo

  alias Toltec.Accounts.User

  def list_users do
    Repo.all(User)
  end

  def get_user!(id), do: Repo.get!(User, id)

  def create_user(attrs \\ %{}) do
    result =
      %User{}
      |> User.registration_changeset(attrs)
      |> Repo.insert()

    case result do
      {:ok, user} -> {:ok, %User{user | password: nil}}
      _ -> result
    end
  end

  def update_user(%User{} = user, attrs) do
    user
    |> User.changeset(attrs)
    |> Repo.update()
  end

  def delete_user(%User{} = user) do
    Repo.delete(user)
  end

  def change_user(%User{} = user) do
    User.changeset(user, %{})
  end
end
```

Next, let's add some seed data to the DB to make our app usable. 

```elixir
# priv/repo/seeds.exs

# users
user =
  Toltec.Accounts.User.registration_changeset(%Toltec.Accounts.User{}, %{
    name: "some user",
    email: "user@toltec",
    password: "user@toltec"
  })

Toltec.Repo.insert!(user)

```

Now is time to build the DB, create tables and insert the seed data.

```bash
mix ecto.reset
```

This command will drop the DB if exists, create it again, run the migrations in order and finally run the seeds.exs file to insert initial data. You should see no errors in the output, and should be similar to this. 

```bash
mix ecto.reset
The database for Toltec.Repo has already been dropped
The database for Toltec.Repo has been created
[info] == Running Toltec.Repo.Migrations.CreateUsers.change/0 forward
[info] execute "CREATE EXTENSION citext;"
[info] create table users
[info] create index users_email_index
[info] == Migrated in 0.0s
[debug] QUERY OK db=2.8ms
INSERT INTO "users" ("email","name","password_hash","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5) RETURNING "id" ["user@toltec", "some user", "$argon2i$v=19$m=65536,t=6,p=1$jdZHJ4HdGSF34hP6iTiPeQ$+PWBp77RP8wxMasVj0wv1wWS33pponixKSEG109V/u8", {{2018, 6, 12}, {22, 35, 55, 411334}}, {{2018, 6, 12}, {22, 35, 55, 411345}}]
```

You can find the source code, with tests, in the repo [here](https://github.com/miguelcoba/toltec-api) in the branch part-01.

After cloning it, run the tests and verify that everything is alright:

```bash
mix test
...................

Finished in 0.3 seconds
19 tests, 0 failures
```

So far we have created the app, the users schema, and some initial user to play with our app. 
That's it for now.