In the previous article, ‘Mail Sending API perks over SMTP in Ruby on Rails using MailGun or SendGrid,’ I explained many advantages of using API over SMTP.
In this article, I will explain more about sending emails using SendGrid SDK in Ruby on Rails, involving SendGrid’s dynamic templates, and even including attachments to the email.
Adding SendGrid SDK to Rails
In Gemfile, add official SendGrid gem:
gem 'sendgrid-ruby', '~> 6.6', '>= 6.6.2'
And run bundle install
.
Dynamic Templates for SendGrid
SendGrid’s more robust feature, in my opinion, is the ability to add dynamic templates. You can create a template using their visual builder or using HTML. Both cases provide a nice responsive preview of the email so that you can create beautiful templates easily.
These templates also provide options for substitutions. For example, you are creating a transactional email, and the receiver’s name is dependent on the person to whom you are sending that email. So mention a variable in the template like {{name}}
, and when you send an email through SDK, send a JSON object for substitution value like below:
{
"name": "Sulman Baig"
}
When you are done creating the template, you will get template_id
. Save that ID for future use.
Get SendGrid’s API Key:
You can get SendGrid’s API Key by following the steps mentioned in their official documentation: https://docs.sendgrid.com/ui/account-and-settings/api-keys#creating-an-api-key
Email Job:
Now that you have template_id
and API Key
, let’s start creating the email job that will send an email to a user in the database using a dynamic template and attachments.
We are assuming for the below code snippet:
-
EXPORT_TEMPLATE
ENV contains the template ID you created -
SENDGRID_API_KEY
ENV contains SendGrid’s API Key. -
@tempfile
is the file saved in local to send as an attachment -
user_id
is the user saved in the database with attributes likename
andemail
.
Now create a file app/jobs/email_job.rb
and add the following code.
You can include the sidekiq gem and call this job asynchronously.
# frozen_string_literal: true
require 'sendgrid-ruby'
#### Example Call
# EmailJob.new.perform(
# @user.id,
# { name: @user.name },
# ENV.fetch('EXPORT_TEMPLATE', nil),
# [
# {
# file: @tempfile.path,
# type: 'application/csv',
# name: @tempfile.path.split('/').last,
# content_id: 'export_file'
# }
# ]
# )
# This is the email job that will be sent to the user
class EmailJob
include SendGrid
# From Email and Name
NOREPLY_FROM_EMAIL = 'no-reply@allwallet.app'
NOREPLY_FROM_NAME = 'All Wallet'
# include sidekiq to call perform as perform_async
def perform(user_id, subsitutions, template_id, attachments = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
# initialize sendgrid api
sg = SendGrid::API.new(api_key: ENV.fetch('SENDGRID_API_KEY', nil))
# we will get to_email from user object saved in db
user = User.kept.find_by(id: user_id)
return unless user
# initialize mail object of sendgrid
mail = Mail.new
# fill 'from' data from the constants mentioned above
mail.from = Email.new(email: NOREPLY_FROM_EMAIL, name: NOREPLY_FROM_NAME)
# personalization is object for email to data and templates
personalization = Personalization.new
# add user data to which email to be sent
personalization.add_to(Email.new(email: user.email, name: user.name))
# add substitutions to template created in sendgrid to replace the variable in template like `{{name}}`
# {
# "name": "Sulman Baig"
# }
personalization.add_dynamic_template_data(subsitutions)
mail.add_personalization(personalization)
mail.template_id = template_id
# If attachments are sent as arguments
if attachments.present? && attachments.is_a?(Array) && attachments.size.positive?
attachments.each do |attachment_input|
attachment = Attachment.new
# attachment has to be sent as base64 encoded string
attachment.content = Base64.strict_encode64(File.read(attachment_input[:file])) # file: path of file saved in local or remote
attachment.type = attachment_input[:type] # type of file e.g. application/csv
attachment.filename = attachment_input[:name] # filename
attachment.disposition = 'attachment'
attachment.content_id = attachment_input[:content_id] # e.g. export_file
mail.add_attachment(attachment)
end
end
begin
# Send Email
sg.client.mail._('send').post(request_body: mail.to_json)
rescue StandardError => e
# TODO: capture exception
end
end
end
Example call for the above job be like:
EmailJob.new.perform(
@user.id,
{ name: @user.name },
ENV.fetch('EXPORT_TEMPLATE', nil),
[
{
file: @tempfile.path,
type: 'application/csv',
name: @tempfile.path.split('/').last,
content_id: 'export_file'
}
]
)
Happy Coding!
Top comments (2)
Where advantages over SMTP?
sulmanweb.com/mail-sending-api-per...