Use Bamboo to Send Email in Phoenix

Sending email from web applications is an inevitability when you have users and need to interact with them.We're going to use Bamboo by Thoughtbot which supports all the major transactional email providers including MailGun and use local html templates for the email content.
Use Bamboo to Send Email in Phoenix

Sending email from web applications is an inevitability when you have users and need to interact with them.

We’re going to use Bamboo by Thoughtbot which supports all the major transactional email providers including MailGun and use local html templates for the email content.

Prerequsites

You’ll need to create a mailgun.com account (or one of the other supported providers) and have a domain and api key ready to go.

Install and Config

Let’s install the library and add configuration for different environments.

Add to mix.exs:

{:jason, "~> 1.1"},
{:bamboo, "~> 1.4"}

And

$ mix deps.get

NOTE: The main mailing module will be MyApp.Mailer so the following configuration will be using it. We’ll eventually create it, so just follow along for now.

Dev Config

We’re going to configure the dev environment to initially use Mailgun for easy local testing, then change it once we’re done.

Open .gitconfig and add:

**/*.secret.exs

Create a config/dev.secret.exs file if you don’t already have one:

import Config
config :my_app, MyApp.Mailer,
  adapter: Bamboo.MailgunAdapter,
  domain: "mg.myapp.com",
  api_key: "my-api-key"

Open config/dev.exs and at the bottom, import the secret file:

...

import_config "dev.secret.exs"

Test Config

Equally important, we don’t want our tests to actually send emails so open config/test.exs and configure Bamboo to use the TestAdapter:

config :my_app, MyApp.Mailer, adapter: Bamboo.TestAdapter

Prod Config

Open config/prod.exs and set the configuration to use environment variables:

config :my_app, MyApp.Mailer,
  adapter: Bamboo.MailgunAdapter,
  domain: {:system, "MAILGUN_DOMAIN"},
  api_key: {:system, "MAILGUN_API_KEY"}

Create Mailer Module

Next we’ll create the mailer module that will import from Bamboo the features needed to send email using the Bamboo.Mailer macro.

Create LIB/mailer.ex with this content:

defmodule MyApp.Mailer do
  use Bamboo.Mailer, otp_app: :my_app
end

Update the modules and otp_app to suit your app.

Create UserNotifier Module

We’re going to assume that you want to send confirmation instructions. And also assume that your User is inside an Accounts context.

Create LIB/accounts/user_notifier.ex with this content:

defmodule MyApp.Accounts.UserNotifier do
  use Bamboo.Phoenix,
  view: MyAppWeb.Accounts.EmailView
  alias MyApp.Mailer
  @reply_to "[email protected]"
  @from "[email protected]"

  def deliver(%Bamboo.Email{} = email, later \\ false) do
    case later do
      true -> Mailer.deliver_now(email)
      false -> Mailer.deliver_later(email)
    end
    {:ok, %{to: email.to, body: email.html_body}}
  end

  def deliver_confirmation_instructions(user, url) do
    base_email()
    |> to(user.email)
    |> subject("Confirm your account")
    |> assign(:user, user)
    |> assign(:url, url)
    |> render("confirmation_instructions.html")
    |> deliver()
  end

  defp base_email() do
    new_email()
    |> put_header("Reply-To", @reply_to)
    |> from(@from)
  end
end

Some highlights about this module:

  • Uses the Bamboo.Phoenix macro to configure the View to use for emails.
  • In this particular case we’re using MyAppWeb.Accounts.EmailView which we will create next.
  • There is a base_email/0 function that sets some defaults, and a deliver/2 function that actually delivers the message and responds with a tuple.
  • deliver_confirmation_instructions/2 sets the email up and renders the template.

We’ll see how to call functions from this module soon but for now let’s continue setting up the view and template.

Create View and Template

Create WEB/views/accounts/email_view.ex with this content:

defmodule MyAppWeb.Accounts.EmailView do
  use MyAppWeb, :view
end

Nothing special here, just allows our mailer to route to the correct template.

Next create WEB/templates/accounts/email/confirmation_instructions.html.eex:

<p>Hi <%= @user.email %>,</p>

<p>You can confirm your account by visiting the url below:</p>

<p><%= @url %></p>

<p>If you didn't create an account with us, please ignore this.</p>

You can see that the @user and @url variables are available because we assigned them in UserNotifier.

Sending Email

With all the ceremony out of the way, let’s see how we can actually call the functions.

In a controller or some other function:

{:ok, _} = UserNotifier.deliver_confirmation_instructions(user, confirmation_url())

Remember that UserNotifier returns a tuple: {:ok, MAP} so we can easily verify that things didn’t go squirrely.

Wrapping Up

This is a basic implementation of email sending using Bamboo in Phoenix. There are some improvements in error handling and other things that can be easily added, but this post is intended to used as a Getting Started guide rather than a detailed implementation.

There are many more details available in the Bamboo Docs.

Hit me up on twitter: @tmartin8080 if you have any issues.

Troy Martin
Senior Engineer working with Elixir and JS.
Keep in the Loop

Subscribe for Updates