DEV Community

Cover image for Rails API Quickstart Guide with PostgresSQL and JWT Tokens
Kailana Kahawaii
Kailana Kahawaii

Posted on • Edited on

Rails API Quickstart Guide with PostgresSQL and JWT Tokens

I recently sat down to create a Rails API and found myself googling like mad. At the end of the day, I had tabs galore and knew there had to be a better way.

This guide is intended for developers comfortable with Ruby on Rails. You will be able to set up a Rails API with CRUD functionality and (hopefully) not open a million tabs in the process.

Preview

  • Create the API with a PostgresSQL Database
  • Add the Project to Github
  • Create the Schema, Models, and Routes
  • Create Relationships Between Models
  • Set Up Cors
  • Create and Reorganize Controllers
  • Create an AuthController to Handle JWT
  • Change the Gem File
  • Hide JWT Tokens
  • Set Up Other Controllers
  • Change Routes
  • Get Everything Up and Running

Let's get started!

Creating the API and Hooking it up to PostgresSQL

You’ll want to start out cding into a folder to store your project.

In your terminal type:

rails new my-project --api --database=postgresql

This creates a new Rails project specifically for API use. This command populates specific gems in your gem folder and forgoes view files. It also hooks your database up to postgresSQL. You do not necessarily need to hook up your database to Postgres, but platforms such as Heroku require it.

Adding the New Project to Github

You can connect your project to your Github at any time. It's recommended to start early so you can save as you go.

In your Github account, create a new repository. Go back to your terminal, and while in your project directory, type the following:

git init git add . git commit -m “first commit” git remote add origin <http url> git remote -v git push origin git checkout -b new_branch 

These commands link your project up to Github and ensure you have a new branch to work on.

Create Your Models, Routes, and Database

This section will be largely determined by the needs of your project. Rails documents a list of generators that you may find useful.

Since we are using bcrypt to encrypt passwords, write a password_digest column on the user table. The resource generator will create a user table, a model, and restful routes for a user.

rails generate resource User username:string password_digest:string

Create another resource generator for a post model. Notice the shorthand for generator and title--the word string can be omitted, but not any other data type.

rails g resource Post title content user_id:integer

Create Relations Between Models

In the users model, just below the class declaration, type the following:

has_many :posts has_secure_password 

Type the following in the post model:

belongs_to :user

A few things to take note of in these models is that has_many relationships take in a plural and the belongs_to does not. The has_secure_password validation gives us access to bycrypt methods in controller (more on that later) which relies on the password_digest from the previously created user table.

Setting up Cors

Cors is middleware that allows your app to make API calls from the front-end.

There’s three steps you need to do to hook this up.

Step 1.

In your project directory, navigate to your gem file and uncomment:

gem ‘rack-cors’

Step 2.

Navigate to config/application.rb and find this class declaration:

class Application < Rails::Application

Beneath the class declaration, paste this code. Do not modify anything else.

config.middleware.insert_before 0, Rack::Cors do allow do origins '*' resource '*', headers: :any, methods: [:get, :post] end end 

Step 3.

In config/initializers/cors.rb uncomment and change

origins ‘examples’

to

origins ‘*’

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 

Cors errors can be incredibly frustrating to debug, so I recommend taking the time to carefully comb through the files to uncomment and add the code laid out in these steps.

Create and Reorganize Your Controllers

When we created our models using the generator command, we also created our controllers. However, these controllers are currently in the wrong directory. What’s up with API/V1? API convention states that you include the version in your url.

You will also need to add an AuthController which will manage user security.

From your terminal, navigate to the controllers directly and type the following:

mkdir api cd api mkdir v1 touch auth_controller.rb 

Move all your controllers into the V1 directory except for ApplicationController.

Precede all your controllers with Api::V1:: like this example:

class Api::V1::UsersController < ApplicationController

This will ensure your routes are consistent.

Create an AuthController to Handle JWT Tokens and User Authentication

We are about half-way into creating our Rails API.

In the newly created AuthController, add the following code:

 class Api::V1::AuthController < ApplicationController skip_before_action :authorized, only: [:create] def create user = User.find_by(username: user_login_params[:username]) if user && user.authenticate(user_login_params[:password]) token = issue_token(user) render json: {user: UserSerializer.new(user), jwt: token} else render json: {error: 'That user could not be found'}, status: 401 end end def show user = User.find_by(id: user_id) if logged_in? render json: user else render json: { error: 'No user could be found'}, status: 401 end end private def user_login_params params.require(:user).permit(:username, :password) end end 

A note on the methods in this class:
The AuthController’s job is to create a session for our user each time they log into the site.

User.authenticate comes from the bcrypt gem.

The skip_before_action validation ensures the user will be able to make an account via the create method. Issue token enables us to use JWT tokens, and is a method we’ll go over later in the application controller. We are using serializers here to better format our JSON.

The logged_in? action in the show method is also inherited from application controller.

Change the Gem File

To get access to some of these actions, go to your gem file and uncomment the following:

gem bcrypt gem 'jbuilder', '~> 2.7' 

In order to use JWT and serializers, add:

gem "jwt", "~> 2.2" gem "active_model_serializers", "~> 0.10.10" 

Please note that your gem versions may vary. Refer to the documentation for the most up-to-date versions.

Set Up and Hide Your JWT Token

Refrain from committing to github until after your secret key and the corresponding file is created.

JWT tokens are industry standard security credentials. These protocols are most commonly used for logging in users by storing a token in their localstorage. You can create your own jwt token by going to the website and changing your-256-bit-secret.

To create your JWT token (and hide it, too) go to your Application controller, add the following:

 class ApplicationController < ActionController::API before_action :authorized def jwt_key Rails.application.credentials.jwt_key end def issue_token(user) JWT.encode({user_id: user.id}, jwt_key, 'HS256') end def decoded_token begin JWT.decode(token, jwt_key, true, { :algorithm => 'HS256' }) rescue JWT::DecodeError [{error: "Invalid Token"}] end end def authorized render json: { message: 'Please log in' }, status: :unauthorized unless logged_in? end def token request.headers['Authorization'] end def user_id decoded_token.first['user_id'] end def current_user @user ||= User.find_by(id: user_id) end def logged_in? !!current_user end end 

Notice the jwt_key variable? We will be hiding this code with the following command.

Hide the secret key by typing in your terminal:

EDITOR="code --wait" rails credentials:edit

Add the hash:

jwt_key: your-256-bit-secret

You can also hide other information in the same way. Once you are done adding your secret keys, save and close the file. For a more in-depth look at how Rails credentials work, check out Derya Tanriverdi ‘s tutorial The Secret to Rails.

You are now free to resume committing to Github.

Having trouble accessing this secret key when migrating to Heroku? Try this command out in your heroku terminal:

heroku config:set RAILS_MASTER_KEY=<your master key>

Your master key is NOT the JWT token key we created, but the Rails master key found in the masterkey file.

Set Up Your Other Controllers

Code the RESTful actions that seem appropriate to your controllers: index, create, show, update, destroy. Your UsersController should also use the jwt_token and current_user methods we created in the application controller.

class Api::V1::UsersController < ApplicationController skip_before_action :authorized, only: [:create] def profile @user = User.find(params[:id]) render json: {user: current_user} end def create @user = User.new(user_params) if @user.valid? @user.save @token = encode_token(@user) render json: { user: @user, jwt: @token } else render json: { error: 'failed to create user' }, status: :not_acceptable end end 

Add Serializers (optional)

Serializers are a good idea for projects with a number of associations. You can generate a serializer from the terminal using the rails g command.

rails g serializer user

This will create a serializer folder and file.

In your newly created serializer, write the following:

class UserSerializer < ActiveModel::Serializer attributes :id, :username end 

Depending on your project's needs, you may need to add additional methods and attributes.

Change Routes

Rails created routes for you through the resources generator. However, these routes are in the wrong places. Reorganize the file and create custom routes for the auth and user controllers.

Rails.application.routes.draw do # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html namespace :api do namespace :v1 do resources :posts resources :users post '/login', to: 'auth#create' get '/current_user', to: 'auth#show' post '/sign_up', to: 'users#create' end end end 

Getting Rails Up and Running

Finally your Rails app is ready to function as an API. Run these last commands from the terminal.

Bundle install Rails db:create Rails db:migrate Rails s 

Summary

And there you have it! We set up a Rails API by connecting it to a postgresSQL database, added JWT tokens for user security, and added serializers. In addition, we connected our project to github, hid our secret key, changed routes, and enabled cors and bcrypt. As a final exercise, use Postman to test out your routes, or use the Rails console to create a few users.

Top comments (1)

Collapse
 
jonyx profile image
jonyx

Hi Kailana
Thanks for this nice posting
I am looking forward to your next one