DEV Community

Nelson Chibukasia
Nelson Chibukasia

Posted on

Integrate Sendgrid using Web API in Ruby on Rails - React application

Sometimes it's always challenging to integrate a mailing system in Rails API for beginners. In this blog, I'll explain step by step on how to integrate Sendgrid in a Ruby on Rails-React application. The blog is beginner friendly and easy to understand.
Setting up Sendgrid mailing system in Rails has always scared beginners in Rails as they think its a complex process. Just to let you know, there is nothing complex with setting up Sendgrid in Rails application.
There are two ways of setting up Sendgrid email system in a web application, that is using Sendgrid Web API and Sendgrid SMTP Relay. In this blog, I will discuss setting up Sendgrid using Web API which is the most recommended way. Let's begin the journey.

Create a Rails application

Lets begin by creating up our rails application. Use the rails new sendgrid --api to create a API rails application.
create rails app
Get into the direcory of the rails application using the command cd sendgrid. Genrate a user resource with the username and email as the attributes. Both are strings. The command is rails g resource user username email.
rails g resource. Run rails db:migrate to run the migrations.
Rails db:migrate. Start the rails server using the command rails s.
rails s
With that done, we shall have our routes, controller, and models already created. Let's go ahead and add some codes in the user controller and user model.

Add Validations in user model

Let's add some validations to make sure the input fields are not empty.

class User < ApplicationRecord
    validates :username, presence: true 
    validates :email, presence: true 
end

Enter fullscreen mode Exit fullscreen mode

Add controller actions in user controller

We are done with the user model, let's now modify our user_controller file to create a new user and validate if username or email is empty. Head to the controllers folder and edit the user_controller.rb file. Let's first create our index method that we shall use to test our API to see if we have our data in properly stored. Edit the userController class to look this this way

class UsersController < ApplicationController
    # Get all users 
    def index 
        users= User.all 
        render json: users
    end
end
Enter fullscreen mode Exit fullscreen mode

Let's add a create method to add a user to the database

# Create a user 
    def create 
        @user = User.create!(user_params)
        render json: {body: @user, message: "User created successfully"}, status: :created
    end
Enter fullscreen mode Exit fullscreen mode

We need to create two private methods, one for permitting user parameters and the other for validating user input.

 # Private methods
    private 

    def user_params
        params.permit(:username, :email)
    end

    # Invalid entity response 
    def invalid_entry(invalid)
        render json: {errors: invalid.record.errors.full_messages}, status: :unprocessable_entity 
    end
Enter fullscreen mode Exit fullscreen mode

Then add a rescue for the invalid user entity at the top of the class

rescue_from ActiveRecord::RecordInvalid, with: :invalid_entry
Enter fullscreen mode Exit fullscreen mode

Our user controller class should be looking similar to this at the moment

class UsersController < ApplicationController
    rescue_from ActiveRecord::RecordInvalid, with: :invalid_entry
    # Get all users
    def index 
        users= User.all 
        render json: users
    end

    # Create a user 
    def create 
        @user = User.create!(user_params)
        render json: {body: @user, message: "User created successfully"}, status: :created
    end

    # Private methods
    private 

    def user_params
        params.permit(:username, :email)
    end

    # Invalid entity response 
    def invalid_entry(invalid)
        render json: {errors: invalid.record.errors.full_messages}, status: :unprocessable_entity 
    end
end
Enter fullscreen mode Exit fullscreen mode

Let's have our user controller looking that way at the moment. We shall come back to edit it when setting up Sendgrid.
Next, let's add CORS by uncommenting the code in cors.rb in initializers folder in config directory. The code in cors.rb should look like this

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins "*"

    resource "*",
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
Enter fullscreen mode Exit fullscreen mode

To make sure our cors configuration is working, we need to uncomment the cors gem in gem file and the run bundle install to install the necessary dependencies. Restart the rails server.

Test APIs using postman

We need to test our APIs and see if they are working perfectly. Open postman and try creating a new user using the post method, and then get all the users created using the get method.

  • Create a user

API test postmant

  • Get all users

API test postmant
Okay, our API endpoints are working fine.
Now, let's get our front-end working

Create React App

The next step is creating a react application with a user simple user signup page. Let's begin by creating our react application. Use the command npx create-react-app client to create a react app called client create react app. Get in the react app directory using the command cd client and run npm start to run the react application.

Create a sign up page

Create a Signup.js file in the clients src folder. Create a functional component called Signup and add the following code in it.

import React from 'react'

function Signup() {
  return (
    <div style={{width: "300px", marginLeft: "auto", marginRight: "auto"}}>
        <h2>User Signup</h2>
        <form style={{display: "flex", flexDirection: "column"}}>
            <label htmlFor='username'>Username:</label> 
            <input type={'text'} id= "username" name='username' /><br/>
            <label htmlFor='email'>Email:</label> 
            <input type={'text'} id= "email" name='email' /><br/>
            <button type='submit'>Submit</button>
        </form>
    </div>
  )
}

export default Signup
Enter fullscreen mode Exit fullscreen mode

Edit the App.js with the following code

import './App.css';
import Signup from './Signup';

function App() {
  return (
    <div className="App">
      <Signup />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Add the functionalities on Signup.js to enable the form to send data to the server using the API endpoint.

import React, { useState } from 'react'

function Signup() {
  const [data, setData] = useState({
    username: '',
    email: '',
  })
  const [errors, setErrors] = useState([])
  const [message, setMessage] = useState('')
  const [success, setSuccess] = useState(false)

  function handleChange(e){
    let name = e.target.name 
    let value = e.target.value 
    setData({...data, [name]: value})
  }

  // POST user data
  function handleSubmit(e){
    e.preventDefault()
    fetch('http://localhost:3000/users',{
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(data)
    }).then(res=>{
        if(res.ok){
            res.json().then(user=>{
                setMessage(user.message)
                setSuccess(true)
            })
        }else{
            res.json().then(err=>setErrors(err.errors))
        }
    })
  }
  return (
    <div style={{width: "300px", marginLeft: "auto", marginRight: "auto"}}>
        <h2>User Signup</h2>
        {success? <h3 style={{color: "green"}}>{message}</h3>:
        <div>
            {errors.map(err=><p key={err} style={{color: "red"}}>{err}</p>)}
        </div>}
        <form style={{display: "flex", flexDirection: "column"}} onSubmit={handleSubmit}>
            <label htmlFor='username'>Username:</label> 
            <input type={'text'} id= "username" name='username' onChange={handleChange} /><br/>
            <label htmlFor='email'>Email:</label> 
            <input type={'text'} id= "email" name='email' onChange={handleChange}/><br/>
            <button type='submit'>Submit</button>
        </form>
    </div>
  )
}

export default Signup
Enter fullscreen mode Exit fullscreen mode

Set up sendgrid

First, you need to create an account with sendgrid. Fill in all the details required while setting the account. Wait for your account to be verified. NOTE: Any user can create the account even if you don't have a company or organization. Once your account has been verified and you can view your dashboard, on the left menu, click on email API and the Integration guide. Your dashboard will display two options for email set up. Use Web API and click choose to select it.
Web API select.
Next, select Ruby as your language

Select Ruby
Enter the name of your API key and click create API to create the key. Follow the instructions on creating the environment variable that shall store the API key. Create the environment variable in the root of your rails application. echo "export SENDGRID_API_KEY='YOUR API KEY'" > sendgrid.env
Add the file to the git ignore so that it is not sent to your github account. This is done to avoid exposing your API key which on exposure, may lead to suspension of your Sendgrid account. echo "sendgrid.env" >> .gitignore
Set the source source ./sendgrid.env. We will later move this API key to a credentials.yml.enc to keep it safe for use.
Next, add the sendgrid gem, gem 'sendgrid-ruby' to the gem file and run bundler or bundle install to install the dependencies. Restart the rails server. Copy the code given under send your first email into a ruby file in your application. Replace the sample details given with your details and then try and run the file separately on the terminal using the command, ruby filename.rb. You will get an email sent to the "to" email. The next step is just moving the code to a class and reusing the class where necessary.

Add the API key into the credentials.yml.enc file

We have come a long way and we are almost there, hang in tight, don't let go. The credentials.yml.enc file (Located in the config directory) is encrypted and therefore cannot be edited directly. To edit the file, type the following command on the terminal EDITOR="code --wait" rails credentials:edit. Code basically represents the code editor you are using. I'm using VS code thus setting the editor to code. You can read more about editing this file on rails Custom Credentials or stackoverflow. When the file opens on your code edit in the edit mode, add the sendgrid API key as follows: sendgrid: YOUR API KEY. The API key should not be in quotes. Save and exit from the file to save changes.

Create the email_service.rb file

I'll have the email file created in a services folder in the apps folder. Create services folder and add in an email_service.rb file. Create a class matching the file name as it is a convention in ruby. in the class, create a class method called class and pass in the parameters; from, to, subject, and content. Then copy the code from sendgrid and edit it as follows. Your class should look like this.

require 'sendgrid-ruby'
include SendGrid
class EmailService 
    def self.call from:, to:, subject:, content:
        from = Email.new(email: from)
        to = Email.new(email: to)
        subject = subject
        content = Content.new(type: 'text/plain', value: content)
        mail = SendGrid::Mail.new(from, subject, to, content)

        sg = SendGrid::API.new(api_key: Rails.application.credentials.sendgrid)
        response = sg.client.mail._('send').post(request_body: mail.to_json)
        puts response.status_code
        puts response.body
        puts response.headers
        puts Rails.application.credentials.sendgrid
    end
end

Enter fullscreen mode Exit fullscreen mode

Note the following changes from the code

  • This code has been changed to get the API key set in the credentials.yml.enc file sg = SendGrid::API.new(api_key: Rails.application.credentials.sendgrid)
  • The code on mail = SendGrid::Mail.new(from, subject, to, content) may slightly look different from the one provided on sendgrid account. The code from sendgrid looks like this mail = Mail.new(from, subject, to, content). I made the changes since the code from sendgrid, when I moved it to the class, it was showing an error about missing parameters. Changing the code to look as the one above solved the problem. Edit the user_controller to send mail on user signup The last step, edit the user controller by adding the email service. We are using the EmailService class to send the email. We call the call class method of EmailService on the class and pass in the arguments. Edit the code in the create method to look as follows.

def create 
        @user = User.create!(user_params)
        @from = "useyouremail@gmail.com"
        @subject = "New User Account"
        @content = "Thank you for registering with us #{@user.username}. Your account has been created successfully"
        EmailService.call(from: @from, to: @user.email, subject: @subject, content: @content)
        render json: {body: @user, message: "User created successfully"}, status: :created
    end
Enter fullscreen mode Exit fullscreen mode

Your UserController class should look like the one below

class UsersController < ApplicationController
    rescue_from ActiveRecord::RecordInvalid, with: :invalid_entry
    # Get all users
    def index 
        users= User.all 
        render json: users
    end

    # Create a user 
    def create 
        @user = User.create!(user_params)
        @from = "useyouremail@gmail.com"
        @subject = "New User Account"
        @content = "Thank you for registering with us #{@user.username}. Your account has been created successfully"
        EmailService.call(from: @from, to: @user.email, subject: @subject, content: @content)
        render json: {body: @user, message: "User created successfully"}, status: :created
    end

    # Private methods
    private 

    def user_params
        params.permit(:username, :email)
    end

    # Invalid entity response 
    def invalid_entry(invalid)
        render json: {errors: invalid.record.errors.full_messages}, status: :unprocessable_entity 
    end
end
Enter fullscreen mode Exit fullscreen mode

Try and register a user with a valid email and check the rails server if the status printed on the terminal is 200s, which means an email has been sent successfully.
status code 202
User Signup

sample user registration
Signup email sent

sent email
Github repo link
That's it, bye and Happy Coding!
bye

Top comments (0)