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 adeliver/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.