Sending emails with Elixir/Phoenix

And Mailgun

Stephan Bakkelund Valois
7 min readJul 2, 2016

Hello, and welcome to my tutorial blog on how to send emails with Phoenix. Sending emails are very easy with Phoenix and Mailgun. The application we’re going to build will be a skeleton application which can be used as a contact form, support form etc.

We’ll start by creating a new project:

~$ mix phoenix.new emailapp
* creating emailapp/config/config.exs
* creating emailapp/config/dev.exs
......
Fetch and install dependencies? [Yn] y
* running mix deps.get
* running npm install && node node_modules/brunch/bin/brunch build

While we’re at it, let’s also create our database with ‘mix ecto.create’:

~/emailapp$ mix ecto.create
The database for Emailapp.Repo has been created

If we run our app with mix phoenix.server and visit localhost:4000, we can see our freshly made application. Sweet.

We will be sending emails through Mailgun. The first thing you need to do, if you haven’t done it yet, is to create a new Mailgun account at www.mailgun.com. This will also provide you with a sandbox domain you can use for development purposes. Once your account is created, it’s time to focus on our app.

We’re going to create an app where we can send a simple email to whomever we’d like. Let’s start by creating our user interface.

First, we’ll write our controller, routes, and view:

# web/controllers/email_controller.exdefmodule Emailapp.EmailController do
use Emailapp.Web, :controller
def index(conn, _params) do
render(conn, “index.html”)
end
end
# web/router.ex
......
scope “/”, Emailapp do
pipe_through :browser # Use the default browser stack
get “/”, PageController, :index
resources “/email”, EmailController
end
......
# web/views/email_view.exdefmodule Emailapp.EmailView do
use Emailapp.Web, :view
end

If we now go to http://localhost:4000/email, we can see our freshly rendered index template:

Now that our controller, view, routes and template are in order, we should probably start thinking about what form fields we want to populate our index template with. For sending emails, we know that we need a recipient, a subject, and the content of the email itself. Let’s use the Phoenix model generator to generate our email model:

~/emailapp$ mix phoenix.gen.model Email emails recipient:string subject:string content:string
* creating web/models/email.ex
* creating test/models/email_test.exs
* creating priv/repo/migrations/20160701171455_create_email.exs
Remember to update your repository by running migrations:$ mix ecto.migrate

If we take a look in our email model file, we can see our freshly generated schema:

# web/models/email.ex
......
schema “emails” do
field :recipient, :string
field :subject, :string
field :content, :string
timestamps()
end

Perfect. Let’s migrate it into our database:

~/emailapp$ mix ecto.migrate
Compiling 1 file (.ex)
Generated emailapp app
20:12:55.352 [info] == Running Emailapp.Repo.Migrations.CreateEmail.change/0 forward20:12:55.352 [info] create table emails20:12:55.360 [info] == Migrated in 0.0s

Awesome, now let’s see if we can get that email form working, first we need to add a bit more to our controller index action:

# web/controllers/email_controller.exdefmodule Emailapp.EmailController do
use Emailapp.Web, :controller
alias Emailapp.Email
def index(conn, _params) do
changeset = Email.changeset(%Email{})
render(conn, “index.html”, changeset: changeset)
end
end

We make an alias to Emailapp.Email to keep our code a little bit more DRY, and we add our changeset to a new changeset variable, and make it available in our view templates.

# web/templates/email/index.html.eex<h2>Send an email</h2>
<%= form_for @changeset, email_path(@conn, :create), fn f -> %>
<div class=”form-group”>
<%= text_input f, :recipient, placeholder: “Recipient”,
class: “form-control” %>
</div>
<div class=”form-group”>
<%= text_input f, :subject, placeholder: “Subject”,
class: “form-control” %>
</div>
<div class=”form-group”>
<%= textarea f, :content, placeholder: “Content”,
class: “form-control”, rows: 10 %>
</div>
<%= submit “Send Email”, class: “btn btn-primary”%>
<% end %>

We create our form with 3 fields, ‘recipient’, ‘subject’, and ‘content’

Cool. Now, if we try to send an email, we’ll just get an error telling us we’re missing a create action. Let’s write it next:

# web/controllers/email_controller.ex
......
def create(conn, %{“email” => email_params}) do
changeset = Email.changeset(%Email{}, email_params)
case Repo.insert(changeset) do
{:ok, email} ->
conn
|> put_flash(:info, “Email sent”)
|> redirect(to: email_path(conn, :index))
{:error, changeset} ->
conn
|> put_flash(:error, “Something went wrong”)
|> render(“index.html”, changeset: changeset)
end
end
......

So we pattern match and fetch our email params from the Plug.Conn struct, and build our changeset with our email struct and the email params we got from our Plug.Conn. If everything goes as planned, we’ll add it into our database. If not, we’ll receive an error and the index template gets re-rendered.

This is pretty sweet, our emails are stored in our database. Let’s write a very simple email history. We need to fetch the emails from our database and make them available in our index action:

# web/controllers/email_controller.exdef index(conn, _params) do
changeset = Email.changeset(%Email{})
emails = Repo.all(Email)
render(conn, “index.html”, changeset: changeset, emails: emails)
end
......

We collect all our emails and store them in the variable ‘emails’, we also make them available in our view template. Let’s add our email history to our template:

# web/templates/email/index.html.eex
......
<h2>Email history</h2>
<%= for email <- @emails do %>
<strong>To:</strong> <%= email.recipient %>
<strong>Subject:</strong> <%= email.subject %>
<hr>
<% end %>

Looks pretty sweet! I’m sure this will win us a design award! Well, you may be aware that we aren’t actually sending any emails yet, however, we have a “great” looking user interface for sending emails. We should start looking into integrating Mailgun into our app. The first thing we need to do is to install the Mailgun package. Let’s add it to our dependencies:

# mix.exs
......
defp deps do
[{:phoenix, “~> 1.2.0”},
{:phoenix_pubsub, “~> 1.0”},
{:phoenix_ecto, “~> 3.0”},
{:postgrex, “>= 0.0.0”},
{:phoenix_html, “~> 2.6”},
{:phoenix_live_reload, “~> 1.0”, only: :dev},
{:gettext, “~> 0.11”},
{:cowboy, “~> 1.0”},
{:mailgun, “~> 0.1.2”}]
end
......

and install it with ‘mix deps.get’. If you get an error saying ‘Hex dependency resolution failed’, just run ‘mix deps.update’, and we’ll be good to go. We also need to restart our application, since we have to restart our application every time a config and lib file havs been changed. Cool. Let’ start look at writing our email module.

When you created your Mailgun account, a sandbox domain was set up to you for development purposes. We are going to need the API base URL and the API key. we’ll add it to our application’s config file:

# config/config.exs
......
config :emailapp,
mailgun_domain: “https://api.mailgun.net/v3/sandboxYOURDOMAIN.mailgun.org",
mailgun_key: “key-YOURKEY”

Now, you should never store such secret keys in your source code. A much better and more secure way is to use environment variables, or add a secret config file that you can hide in gitignore. However, since this tutorial is only for learning purposes and demonstration, we’ll make an exception.

We’ll add our mailer module in lib/sendapp/ folder, and call our module ‘mailer’:

# lib/emailapp/mailer.exdefmodule Emailapp.Mailer do
use Mailgun.Client,
domain: Application.get_env(:emailapp, :mailgun_domain),
key: Application.get_env(:emailapp, :mailgun_key)
def my_first_email(email_address) do
send_email to: email_address,
from: “test@example.com”,
subject: “My first email”,
text: “This is an email send with Phoenix and Mailgun”
end
end

First, we bring the Mailgun module into our own mailer module, and at the same time make our Mailgun domain and key available. Our first function will send an email to whichever email address we give it, with a subject and a content in plain text. Let’s try it out:

iex> Emailapp.Mailer.my_first_email(“your@email.com”)
{:ok,
“{\n \”id\”: \”<lots-of-numbers.mailgun.org>\”,\n \”message\”: \”Queued. Thank you.\”\n}”}

Sweet. If you go to your Mailgun account and check your logs, you’ll see that the email was sent, and on it’s way to your email inbox! Now, how can we use our email module with the user interface we created earlier?

Let’s write another function, very similar to ‘my_first_email/1’:

# lib/emailapp/mailer.ex
......
def send_email(recipient, subject, content) do
send_email to: recipient,
from: “your@email.com”,
subject: subject,
text: content
end
......

Ok. Looks pretty similar, right? We have three arguments here, and we’re going to use these to send our information from the form to our mailer function. In our mailer controller, we need to fetch some more information from our conn struct. We need to specifically fetch the recipient, subject and content. Let’s do some pattern matching:

# web/controllers/email_controller.ex
......
def create(conn, %{“email” => email_params,
“email” => %{“recipient” => recipient,
“subject” => subject, “content” => content}}) do
......
end
......

So we fetch our recipient’s email address, the subject and the content. Now, all we need to do is add one more line of code to our create action:

# web/controllers/email_controller.ex
......
def create(conn, .....) do
......
case Repo.insert(changeset) do
{:ok, email} ->
Emailapp.Mailer.send_email(recipient, subject, content)
conn
|> put_flash(:info, “Email sent”)
|> redirect(to: email_path(conn, :index))
{:error, changeset} ->
conn
|> put_flash(:error, “Something went wrong”)
|> render(“index.html”, changeset: changeset, emails: emails)
end
end
......

So, we send our values to our send_email/1 function if ‘Repo.insert(changeset)’ is successfully executed, and our Mailgun package handles the rest. If we now look at our Mailgun log, we can see that our email is sent!

We can also send HTML emails with Mailgun, by adding one more line to our mailer function:

# lib/emailapp/mailer.ex
......
def send_email(recipient, subject, content) do
send_email to: recipient,
from: “your@email.com”,
subject: subject,
text: content,
html: content
end

We simply added a ‘html’ to our function. Our mailer will send both a text and a HTML email to our recipient.

This is all for now, thanks for reading!

Until next time
Stephan Bakkelund Valois

--

--