guide, elixir, phoenix, pow, mailer, module, sendgrid, development, test

Add Mailers to Phoenix with Bamboo

In the previous guide, we used Pow to add user Authentication features to a greenfield Phoenix app.   We're going to follow it up by adding a mailer system to our app using another well supported and documented library call Bamboo by the team behind ThoughBot.

Testable, composable, and adapter based Elixir email library for devs that love piping

Overview

Pre-Requisites

  • Skimmed through or followed along the Previous Guide
  • Familiar with Phoenix and have a local dev env set up

Adding Bamboo

Let's start by adding the package. Open mix.exs and add:

defp deps do
  [
    {:bamboo, "~> 1.3"} 
  ]
end

And run mix deps.get.

There are some details to adding a Mailer for Pow in their docs: https://github.com/danschultzer/pow/blob/master/guides/configuring_mailer.md

Create Mailer Module

Create a new file WEB_PATH/mailer.ex with the content:

defmodule PhxPowWeb.Mailer do
  use Pow.Phoenix.Mailer
  use Bamboo.Mailer, otp_app: :phx_pow
  require Logger

  import Bamboo.Email

  def cast(%{user: user, subject: subject, text: text, html: html}) do
    new_email()
    |> to(user.email)
    |> from("[email protected]")
    |> subject(subject)
    |> html_body(html)
    |> text_body(text)
  end 

  def process(email) do
    deliver_now(email)
  end 
end

Though you can have multiple mailers, this will be our main Mailer module for the app.

Next, open config/config.exs and add themailer_backend key to the Pow configuration:

config :phx_pow, :pow,
  user: PhxPow.Users.User,
  repo: PhxPow.Repo,
  extensions: [PowResetPassword, PowEmailConfirmation],
  controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks,
  web_module: PhxPowWeb,
  mailer_backend: PhxPowWeb.Mailer 

Bamboo Adapters

There are many different Bamboo Adapters that are baked into the library and we're going to set them differently depending on the environment.

https://github.com/thoughtbot/bamboo#available-adapters

Production Environment:

Open config/prod.exs and add:

# Adapter for sendgrid
config :phx_pow PhxPowWeb.Mailer,
  adapter: Bamboo.SendGridAdapter,
  api_key: System.get_env("SENDGRID_API_KEY")

This will obviously require the SENDGRID_API_KEY env variable to be set in production.

Test Environment:

Open config/test.exs and add:

# Bamboo mailer config 
config :phx_pow, PhxPowWeb.Mailer,
  adapter: Bamboo.TestAdapter 

Development Environment:

Open config/dev.exs and add:

# Local Adapter for development
config :phx_pow, PhxPowWeb.Mailer,
  adapter: Bamboo.LocalAdapter,
  open_email_in_browser_url: "http://localhost:4000/sent_emails"

The LocalAdapter stores sent emails in memory, and there is an extra configuration key here that will enable sent emails to be viewed in the browser.

To Enable this route, open WEB_PATH/router.ex and add:

defmodule PhxPowWeb.Router do 
  
 # ... pipelines

  if Mix.env == :dev do
    forward "/sent_emails", Bamboo.SentEmailViewerPlug
  end

end

Development Mailer

Now if you go to http://localhost:4000/registration/new and register, a new browser tab should open letting you read and interact with the emails stored in memory.  NOTE: once you restart the server, these emails will be trashed.

Sendgrid in Development

If you're curious about configuring the development mailer to use sendgrid, just swap the adapter from prod to dev, add your api key and fire away.


Conclusion

We were able to add a custom Mailer module and separate the configuration into different environments.   Even though we haven't fully tested the Production Sendgrid configuration, I know it works well out of the box without any further configuration.

Hope you enjoy our Guides.  Follow us on Twitter @phxroad for updates!

Resources

Author image

About Troy Martin

Husband, Father & Software Developer who tinkers with writing and building things.
  • Hamilton, ON