Bamboo is a testable, composable and adapter based email library for Elixir.
You might be thinking, what could possibly be so cool about sending email in Elixir? Can it be any better than what I’ve used in the past?
Hopefully we can show you what the problems were with existing solutions and how we solved them with features unique to Elixir.
Those pesky email addresses
Sometimes you only send to one person and all you need is the address. That’s pretty simple to do
Bamboo.Email.new_email(to: user.email)
But often times you want to also give the person’s name, or send to a list of people. Let’s take an example where users can subscribe to a post. When someone comments on a post, each subscribed user gets an email.
Typically you would need to do something like this:
def new_comment_on_post(comment, recipients) do
recipients = for user <- recipients do
# Emails can either be a string, or like in this example, a 2 item tuple.
{user.name, user.email}
end
new_email
|> to(recipients)
|> subject("New comment on a post you are subscribed to")
|> text_body("There is a new comment")
|> html_body("There is a <strong>new comment</strong>")
end
You would have to do this every time you send an email to recipients. Instead,
Bamboo lets you define a protocol for Bamboo.Formatter
that will let Bamboo
know how to format your recipients. It would look something like this.
defimpl Bamboo.Formatter, for: MyApp.User do
def format_email_address(user, _opts) do
{user.name, user.email}
end
end
Now when you send an email you can just pass a single user, or list of users and Bamboo will format them correctly.
new_email
|> to(recipients) # Will format the users with the Bamboo.Formatter protocol
Defaults come for free
In a lot of libraries it can be hard to use things together, but with Bamboo and Elixir’s pipe operator, you get defaults for free. Set a default layout, default from address or whatever you need by using functions and Elixir’s much loved pipe operator.
defmodule MyApp.Email do
import Bamboo.Email
def welcome_email(recipient) do
base_email
|> to(recipient)
|> subject("Welcome!")
|> text_body("Welcome to the app")
end
defp base_email do
new_email
|> from("myapp@thoughtbot.com")
|> put_header("Reply-To", "support@thoughtbot.com")
end
end
Testing used to be a pain
Bamboo was designed to make unit and integration testing simple. Because composing emails is split from actually delivering the emails, unit testing is very straightforward.
defmodule MyApp.EmailTest do
use ExUnit.Case
test "welcome email" do
user = %User{name: "Paul", email: "paul@gmail.com"}
email = MyApp.Email.welcome_email(user)
assert email.to == user
assert email.subject == "This is your welcome email"
# The =~ asserts that the left hand side contains the text on the right
assert email.html_body =~ "Welcome to the app"
end
end
Then when you want to integration test, you can assert that the welcome email was sent. Here’s an example of a controller for handling new registrations in Phoenix.
defmodule MyApp.RegistrationControllerTest do
use MyApp.ConnCase
use Bamboo.Test
test "sends welcome email" do
user_params = [name: "Paul", email: "paul@gmail.com"]
post conn, registration_path(conn, :post), user_params
newly_registered_user = Repo.get_by!(User, user_params)
assert_delivered_email MyApp.Email.welcome_email(newly_registered_user)
end
end
defmodule MyApp.RegistrationController do
use MyApp.Web, :controller
def create(conn, %{"user" => user_params}) do
user = insert_user(user_params)
MyApp.Email.welcome_email(user) |> MyApp.Mailer.deliver_later
redirect(conn, to: "/")
end
end
How do I get that password reset link?
Bamboo comes with a EmailPreviewPlug
for using in Phoenix or other Plug
based frameworks. This lets you see emails that were sent when using
Bamboo.LocalAdapter
. This is great for trying out password reset features,
or seeing how an email will look.
Flexibility is at the core
Adapters make it much easier to switch providers if something goes wrong, they shut down, or you can get a better price elsewhere.
Bamboo ships with adapters for Mandrill and Sendgrid. There are also third party adapters available, and building your own is straightforward for most services.
Keeps things fast
Studies show that speed directly correlates to customer satisfaction and spending. Let’s keep things fast.
Bamboo lets you easily send emails in the background without any dependencies
outside of what comes with Elixir. Just add the
Bamboo.TaskSupervisorStrategy
to your app, and send with
Mailer.deliver_later
.
If you need something more robust you can easily create your own strategy for
delivering later with Bamboo.DeliverLaterStrategy
.
Give it a try
For more examples and in-depth documentation, see the docs on hex.pm.
Get started with Bamboo today, we think you’ll love it.