Send Emails using SendGrid SDK for Ruby on Rails using Dynamic SendGrid Email Templates
Tags: rails, ruby
Published: 2022-09-07 05:53:02

In the previous article, ￼[‘Mail Sending API perks over SMTP in Ruby on Rails using MailGun or SendGrid,’](https://sulmanweb.com/mail-sending-api-perks-over-smtp-in-ruby-on-rails-using-mailgun-or-sendgrid/ "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:
```ruby
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:
```json
{
  "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](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 like `name` and `email`.

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.

```ruby
# 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
```
[GitHub Gist](https://gist.github.com/sulmanweb/eab697301cf8209572c5a95af6543b3a "Send Emails using SendGrid SDK for Ruby in Rails using Dynamic SendGrid Email Templates")

Example call for the above job be like:
```ruby
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!