How to Send Emails From Your Rails Application

So you want to send emails from your Rails app? It's pretty easy and only takes a few minutes to set up.

I'll walk through a basic setup that I use for most of my projects. It's really simple, so let's get right into it.

A Quick Overview of All the Things

Here's everything we'll need to get going today.

  • Mail transfer service
  • Custom domain for our email address
  • A few snippets of Ruby

Why Use Email Service Providers

Why do we need to use a third-party email service?

You don't have to, but you'll have to dive really deep into the world of Mail Transfer and you'll be entirely responsible for the stability and performance of your mail servers. I wouldn't recommend this approach for most people.

How to Learn AWS
How to Learn AWS
Learn AWS the easy way, become a leader on your team.
How to Learn AWS
LEARN MORE

Rails is basically begging us to just use a gem and be done with it, and for good reason.

Third-party services can handle the complexity of mail transfer for us, and Rails makes it super easy to configure with its ActionMailer module.

There are many options out there (SendGrid, Mailgun, Amazon SES). Feel free to look at what services also offer Ruby gems that are frequently updated.

We're going to set up our app with SendGrid.


SendGrid Setup

We'll need to sign up and set up our account. It's pretty straightforward but we'll want to authenticate our domain so we can send emails from our custom domain.

Using a Custom Domain

There should be an option called 'Sender Authentication' in the Settings tab on the left.

Setting up a custom domain is pretty straightforward here. We'll get a series of CNAME records we need to add to our domain.

You'll want to login to wherever your domain is currently hosted and add the new records there, then SendGrid will prompt you to verify that it's been done and in a matter of seconds your domain will be verified!

Get Your API Key

Next, we need to get an API key so we can authenticate with SendGrid and send emails from our application.

Also under the Settings tab on the left, there's an option labeled 'API Keys'.

Click that, create a key and save it somewhere private. Ideally as an environment variable in your app instead of in your source code.

We'll need this key in the next step.


Rails Configuration

ActionMailer handles email sending in Rails and it's really easy to set it up with a third-party service like SendGrid.

We'll want to navigate to our config/environment.rb file and paste this block of code in:

ActionMailer::Base.smtp_settings = {
  :user_name => 'apikey',
  :password => '<%= SENDGRID_API_KEY %>',
  :domain => 'testsuite.io',
  :address => 'smtp.sendgrid.net',
  :port => 587,
  :authentication => :plain,
  :enable_starttls_auto => true
}

The only changes we need to make are the :password value and the :domain. Set the <%= SENDGRID_API_KEY %> to your actual SendGrid API key, and change the 'testsuite.io' domain to your own!

From here on, ActionMailer will use SendGrid to send emails.


How to Actually Send the Emails

We've covered all of the under-the-hood setup stuff. Now how do we trigger an email?

Essentially we need to create a mailer class, give it a view to render, and then call it somewhere to trigger the actual send.

A "mailer" in Rails is like a controller.

It gets called, it renders a view and sends it to the browser, but with email, it sends it to wherever our ActionMailer::Base.smtp_settings points too from the previous step.

Create a Mailer Class

There's some boilerplate so feel free to copy this. I'll walk through what everything does.

class EmailNotifierMailer < ApplicationMailer
  default from: 'hello@testsuite.io'

  def send_welcome_email(user)
    @user = user
    mail(to: @user.email,
    subject: 'Thanks for joining our mailing list' )
  end
end

Again, this is really similar to a controller that would inherit from 'ApplicationController'.

Inherit ApplicationMailer

We define the class and inherit from ApplicationMailer so that it uses ActionMailer.

Define Action Methods

We define a method that we can call to send our email. Just like a controller would define actions and render views, our mailer has actions that render views.

Optional: Include Instance Variables

Just like a controller can pass variables to render in HTML templates, our mailer can pass variables to our email templates.

I've set up our method to take in a user, and I save that user so that my email template can access it.

That's this part: @user = user, and lets our template call @user to render more personal data such as @user.name.

Define the Email's Recipient and Subject

The last part of our mailer is the call to mail(to: @user.email, subject: 'Thanks for joining our mailing list').

This simply defines who the email will be sent to and what the subject line of the email will be.

Overriding Configurations

We can also override some of the configurations we set up in our config/environment.rb.

For example, in that file we set our :domain => 'testsuite.io', but if I wanted to send an email from my personal domain or if I wanted to use a different email service, like Mailgun or MailChimp, I could override that here in my mailer class.

This way only this particular email gets this specific configuration and everything else will continue using my config/environment.rb setup.

How to Override Things

We can pass another parameter called delivery_method_options to our mail call, like so:

mail(to: @user.email,
         subject: "Thanks for signing up!",
         delivery_method_options: delivery_options)

This parameter takes in any of the keys we set up in our config.

I'm passing it a variable called delivery_options so I could define that to look something like this:

delivery_options = { 
    user_name: 'new username',
    password: 'best password',
    address: 'smtp.mailgun.org'
}

For example, I this would change the address from smtp.sendgrid.net to smtp.mailgun.org and this mailer would then use Mailgun to send this email.

Note: I do not recommend ever putting passwords or API keys directly into source code like this. You should take advantage of environment variables and other means to keep secrets out of your source code.


How to Automatically Trigger an Email

Anywhere in our code, we can trigger our new email by calling 'deliver' on our mailer class. We also set up our mailer to take in a user so we could use their email and other fields like their name.

Here's what the call would look like:

EmailNotifierMailer.send_welcome_email(@user).deliver

This will trigger the send and SendGrid will deliver the email to the recipient. It pretty much wraps up the backend parts, the only thing missing is the actual email itself.


Building the Actual Emails

Again, you can think of mailers as controllers and when they get called they automatically look for a view in a specific location.

ActionMailer will pull in the HTML or TXT file from this location, render any variables like @user.name into it, and then send the resulting content to our recipient.

Location of Your Email Templates

First, let's cover where these templates need to go.

The default path is app/view/<mailer name>/<method name>.rb.

Here's an example, for our mailer called EmailNotifierMailer, when we call the send_welcome_email method to trigger an email - it's going to send an email using the template located at:

app/views/email_notifier_mailer/send_welcome_email.html.erb

How to Create Templates

Last but NOT least, the actual email we want to send.

I won't go too deep into this topic since our email templates are regular ERB files just like the views for our website content.

This means we can render dynamic content and embed variables as needed. We can also use partials, layouts, everything else that comes with ERB.

Although email supports HTML or TXT code, the email clients people use don't always support the latest HTML content. To ensure our content renders correctly across all email clients it's generally best to use older HTML standards like <table> elements over <div> elements. But this is also changing and getting better over time.


Final Words

I wrote this guide to help introduce others to how Rails handles emails, but also for myself as a reference to come back to when I have to set up emails in a new project.

It's very much a living document and will be updated frequently if Rails introduces any changes.

Thanks for getting this far. I appreciate everyone that reads this content and really hope I was able to help you out.

Featured
Level up faster
Hey, I'm Nick Dill.

I help people become better software developers with daily tips, tricks, and advice.
Related Articles
More like this
How to Export to CSV with Ruby on Rails
Adding Active Storage to your Rails Project
What is MVC and Why Should I Use It?