Sending email with Elixir and Phoenix 1.5
Tags: elixir, phoenix
Published: 2020-04-30 00:23:41

---
title: "Sending email with Elixir and Phoenix 1.5"
published: true
description: 
tags: [elixir, phoenix]
---



This is part 2 in a series. In [part 1](https://dev.to/joseph_lozano/setting-up-a-new-phoenix-1-5-project-with-phoenix-liveview-309n) we created the users model using a generator. In this section, we'll be building the login flow by hand.

Let's start by creating the following files
```
live/session_live/new.ex
live/session_live/new.html.eex
```

Let's start with our template, `new.html.eex`
```elixir
# lib/feenix_web/live/session_live/new.html.eex
<h1>Log in</h1>
<%= f = form_for :login, "#",
id: "login-form", phx_submit: "login" %>
<div>
  <%= label f, :email %>
  <%= email_input f, :email %>
  <%= error_tag f, :email %>
</div>

<div>
  <%= submit "Save", phx_disable_with: "Saving..." %>
</div>
</form>
```

This is a form very similar to our New User form, but we only need the email.

Next, let's do the LiveView

```elixir
# lib/feenix_web/live/session_live/new.ex
defmodule PokerWithFriendsWeb.SessionLive.New do
  use PokerWithFriendsWeb, :live_view

  alias PokerWithFriends.Accounts
  alias PokerWithFriends.Accounts.User

  @impl true
  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  def handle_event("login", %{"login" => %{"email" => email}}, socket) do
    socket =
      case Accounts.get_user_by(email: email) do
        nil ->
          socket
          |> put_flash(:error, "You need to create an account first")
          |> push_redirect(to: Routes.user_new_path(socket, :new))

        user ->
          # TODO: error handling for email
          Accounts.deliver_login_email(user)
            socket
            |> put_flash(:info, "Login email sent")
            |> push_redirect(to: socket.assigns.return_to)
          end
      end

    {:noreply, socket}
  end
end
```

This isn't as bad as it first looks

`mount` is doing nothing special

`handle_event/3` is receiving the login event with the email. It then checks if a user with that email exists. If not, it forwards to the User/new page, if so, we call `Accounts.deliver_login_email(user)`, leaving a note for ourselves to handle errors. In both cases, we add a helpful flash message

Next, let's create those functions in our Account module.

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

def get_user_by(params) do
  Repo.get_by(User, params)
end

def deliver_login_email(%User{} = _user) do
  require Logger
  Logger.warn "deliver_login_email not implemented"
  :ok
end
```

Ok, we need to implement our login email. For that, I am going to be using [swoosh](https://github.com/swoosh/swoosh)


Add swoosh and phoenix_swoosh to your mix deps
```elixir
# mix.exs
defp deps do
  ...
  {:swoosh, "~> 0.25"},
  {:phoenix_swoosh, "~> 0.2"},
  ...
end
```

In the our config, let's set swoosh to use the local adapter
```elixir
config :feenix, Feenix.Mailer, adapter: Swoosh.Adapters.Local
```

Then, we need to define a mailer. I'll put it in the top level of feenix_web
```elixir
# lib/feenix_web/mailer.ex
defmodule Feenix.Mailer do
  @moduledoc false
  use Swoosh.Mailer, otp_app: :feenix
end
```

In our router, there is a line `if Mix.env() in [:dev, :test] do` which has the live_dashboard route. Inside that `if`, let's add a new dev scope for our mailbox

```elixir
scope "/dev" do
  pipe_through [:browser]
  forward "/mailbox", Plug.Swoosh.MailboxPreview, base_path: "/dev/mailbox"
end
```

Next, we need to create our emails. I'll do this in a new feenix_web/emails folder
```elixir
# lib/feenix_web/emails/login_email.ex
defmodule FeenixWeb.LoginEmail do
  @moduledoc false
  use Phoenix.Swoosh,
    view: FeenixWeb.EmailView,
    layout: {FeenixWeb.LayoutView, :email}

  def login_email(user) do
    new()
    |> to(user.email)
    |> from({"Feenix", "login@localhost"})
    |> subject("Your login link")
    |> render_body("login_email.html", %{})
  end
end
```

And as you can probably guess from looking at the code, let's add a `login_email.html.eex` template.



Here is your login link:
```html
<div>
  <a href="localhost:4000" />
<div>
```

We haven't created our token yet, but this is a great start.


Now, let's head back to our accounts context, and finish the `deliver_login_email` function.

```elixir
# lib/feenix/accounts.ex
alias FeenixWeb.{LoginEmail, Mailer}
def deliver_login_email(%User{} = user) do
  user
  |> LoginEmail()
  |> Mailer.deliver()
end
```

Now, let's head over to the `/dev/emails` endpoint we set up and verify that we got the email!


In part 3, we will create the tokens that will actually allow us to login. Until then, happy coding!