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.

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

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

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

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.

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.

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 .

For more examples and in-depth documentation, see the docs on hex.pm.

Get started with Bamboo today, we think you’ll love it.