Rails Mailers with SendGrid

Action Mailer

Action Mailer is a Rails framework that allows your application to generate outgoing email and process incoming email. Mailer models generate emails in much the same way as Rails controllers render views.

Create a new Rails app

Start by creating a new Rails app (using the postgresql database adaptor, since we want to deploy to Heroku):

rails new badgermail -d postgresql
cd badgermail

Initialize a new Git repository

git init
git add .
git commit -m "New repo"

Now let's scaffold a User resource:

rails generate scaffold User name:string email:string
rake db:create
rake db:migrate

Generate a mailer

Generate a new mailer, which we'll call 'UserMailer' (you can call the mailer whatever you want):

rails generate mailer UserMailer

This will create all the following files in your Rails tree:

File Description Rails MVC equivalent
app/mailers/user_mailer.rb Mailer Controller
app/mailers/application_mailer.rb Application mailer app/controllers/application_controller.rb
app/views/user_mailer Our directory for email views to be sent from the application Other app/views folders
app/views/layouts/mailer.text.erb An email view wrapper template for email clients that do not allow receipt of HTML emails app/layouts/application.html.erb
app/views/layouts/mailer.html.erb An email view wrapper template for email clients that do allow receipt of HTML emails app/layouts/application.html.erb

Set up mailer actions

Just as we set up actions for page templates (views) in our controller, we need to set up actions for email views in our mailer. Let's set one up to welcome new users on sign-up. A basic use of the mailer model is to:

  1. Set up any defaults to apply to all emails using this class;
  2. Initialize any instance variables we want to be available to email templates;
  3. Call the mail method.
# In app/mailers/user_mailer.rb

default from: "[email protected]"

def welcome(user)
  @user = user #@user will be whatever user we pass in to the 'welcome' method
  mail( :to => @user.email, :subject => "Welcome to Badgertown!", :cc => "[email protected]" )
end

You can pass a number of options into the mail method, but for now let's keep it simple and just specify the recipient, bcc and subject-line for the email we want to send. See the Rails Guides and Ruby On Rails API docs for more on the options you can pass into the mail method.

Creating a view for your email

Now that we have our mailer action set up, let's create a template for our email. Rails will assume the view to be rendered in the email matches the name of the method in our user_mailer.rb unless we specify otherwise in the mail method using the template_path and template_name options.

Create the mailer views

touch app/views/user_mailer/welcome.html.erb
touch app/views/user_mailer/welcome.text.erb

Populate the html template for email clients that accept html emails

Our mailer.html.erb file will contain the basic template that wraps our emails, so our welcome.html.erb template will be pretty straightforward.

<!-- in app/views/user_mailer/welcome.html.erb  -->

    <h1>Thanks for signing up, <%= @user.name %>!</h1>
    <p>You've taken your first step into a larger world.</p>
    <p>Click <%=link_to 'here', edit_user_url(@user) %> to edit your profile.</p>

Populate the text template for email clients that do not accept html emails

Thanks for signing up <%= @user.name %>!

You've taken your first step into a larger world.

Click <%=link_to 'here', edit_user_url(@user) %> to edit your profile.

Call the mailer from the controller

We want the 'welcome' email to be delivered when a new user is created. So, let's call the mailer from the create action in our users_controller.rb

# In app/controllers/users_controller.rb

  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        # The line below is the only additional line we need in our Users controller.
        UserMailer.welcome(@user).deliver_now
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

SendGrid

Why SendGrid?

Our application still requires a server to handle the transmission of emails, but Heroku doesn't support SMTP. So, while we can host our application on Heroku, we need to use a third party SMTP server. We'll be using SendGrid for this - SendGrid's free plan allows you to send up to 12,000 emails per month at no charge.

Sign up for SendGrid

Determine how you want to authenticate with SendGrid

SendGrid gives you three options for authenticating your identity when sending requests to their SMTP server:

  1. Using your SendGrid login credentials - use your SendGrid credentials;
  2. Using additional SendGrid user credentials - setting up additional credentials linked to your SendGrid account.
  3. Using SendGrid API keys - setting up API keys linked to your SendGrid account.

This guide will assume you are using your SendGrid login credentials.

Using your SendGrid login credentials

You don't need to do anything. Just remember your SendGrid username and password and configure them in your application and environments as set out below.

Using additional SendGrid user credentials

These can be used in exactly the same way as your SendGrid credentials, but allow you to set up multiple credentials (eg, one set for each application, or one set for each developer on your team). To do this:

  • Go to the SendGrid Dashboard.
  • Click Settings > Credentials.
  • Click Add New Credential.
  • Complete the form, selecting 'Full Access' for at least 'Mail Send'.
  • Complete the process described below, using your newly created credentials in place of your SendGrid username and password.
Using SendGrid API keys

If you want to use API keys - rather than a username and password - to authenticate with SendGrid

  • Go to the SendGrid Dashboard.
  • Click Settings > API Keys.
  • Click Create API Key > General API Key.
  • Complete the form, selecting 'Full Access' for at least 'Mail Send'.
  • In the process described below:
    • Enter the string "apikey" as the SendGrid username; and
    • Enter your API Key as the SendGrid password.

Specify SendGrid's smtp server in our Action Mailer settings

First, create an initializer for our mailers:

touch config/initializers/mail.rb

Configure the mail initializer to use SendGrid's smtp server:


# In config/initializers/mail.rb

ActionMailer::Base.raise_delivery_errors = true

ActionMailer::Base.smtp_settings = {
  :user_name => Rails.application.secrets.sendgrid_username,
  :password => Rails.application.secrets.sendgrid_password,
  :domain => ENV["RAILS_ENV"] == "development" ? "localhost" : "heroku.com",
  :address => 'smtp.sendgrid.net',
  :port => 587,
  :authentication => :plain,
  :enable_starttls_auto => true
}

ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.default charset: "utf-8"

Notice that our smtp settings to not explicitly state our SendGrid username & password, but rather point to Rails.application.secrets (which in turn references an environment variable). There is a fairly gigantic problem with storing credentials like your SendGrid username and password directly in your mail.rb file or anywhere else - if your project is on GitHub, and your GitHub repo is public, those credentials will be exposed to the world. This can be completely fucked up - you run the risk of people stealing your credentials and using them to rack up enormous bills at your expense.

To prevent this from happening, you should always store credentials and API keys in environment variables, or in secrets.yml (or another file that will not be added to Git). Using other hosts, we might be able to store our credentials in our secrets.yml and then add that file to .gitignore, but this won't work on Heroku, since we can only deploy to Heroku by git push-ing to our Heroku remote.

Configure Heroku for SendGrid

Create a Heroku application

heroku create badgermail
# Creating badgermail... done, stack is cedar-14
# https://badgermail.herokuapp.com/ | https://git.heroku.com/badgermail.git

This will create a remote repo on heroku, which you can confirm in terminal using the Heroku CLI.

git remote -v
# heroku  https://git.heroku.com/badgermail.git (fetch)
# heroku  https://git.heroku.com/badgermail.git (push)

Setting environment variables for our credentials.

In order to use SendGrid, we need our application to send our SendGrid credentials with every request. To get our application to do this, we need to configure those credentials in each environment (Production, Development, maybe Test, etc)

Development Environment
Set credentials as environment variables in secrets.yml
development:
  secret_key_base: [whatever_your_huge_secret_key_is]
  sendgrid_username: <%= ENV["SENDGRID_USERNAME"] %>
  sendgrid_password: <%= ENV["SENDGRID_PASSWORD"] %>
  sendgrid_domain: localhost

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  sendgrid_username: <%= ENV["SENDGRID_USERNAME"] %>
  sendgrid_password: <%= ENV["SENDGRID_PASSWORD"] %>
  sendgrid_domain: heroku.com

Note: We could have just set these in our mail.rb smtp settings (setting username and password as :user_name => ENV["SENDGRID_USERNAME"] etc in that file, instead of :user_name => Rails.application.secrets.sendgrid_username), but using secrets for credentials is a good habit to get into.

Add your SendGrid Credentials as environment variables in Terminal

Open your bash profile.

atom ~/.bash_profile

Add the following to your bash profile

# In .bash_profile
 export SENDGRID_USERNAME="mrbadger"
 export SENDGRID_PASSWORD="chicken"

Since these are now part of your bash profile, you don't need to repeat this for future applications.

To confirm that this worked, restart Terminal and run the following command.

ENV | grep SENDGRID
# SENDGRID_USERNAME=mrbadger
# SENDGRID_PASSWORD=chicken
Test the mailer in the development environment
  • rails s
  • go to localhost:3000/users/new
  • create a user
  • check the server logs
  • check your email

Production Environment

Configure the Heroku app with your SendGrid credentials
If you have a SendGrid account

If you have a SendGrid account, you can manage all your mailers using those credentials.

In the root directory of your git repo, run the following:

heroku config:set SENDGRID_USERNAME=mrbadger
heroku config:set SENDGRID_PASSWORD=chicken

You can also set these in the Heroku web app by clicking on your project, going to settings and adding them as key-value pairs in your Config Variables.

If you don't have a SendGrid account

If you don't have a SendGrid account, you can get Heroku to create one for you using the Heroku addon creator (this will only work if Heroku have your credit card details, even though we're only going to be creating an unpaid, 'Starter' tier account).

heroku addons:create sendgrid:starter
Stage, commit and push your project to Heroku
git add .
git commit -m "Heroookku-san"
git push heroku master
Start the server and test in production
heroku open
# browse to /users/new and create a new user.

Mailers with Heroku and SendGrid - documentation and further reading

results matching ""

    No results matching ""