Ruby on Rails Developer Series: Spinning Up a JSON API in Minutes

Written by: Evan Glazer

Welcome to the first of four Ruby on Rails Developer Series. In this series, the goal is to outline how to strengthen your API with Postgres, how to dockerize your project and add security layers to mitigate attacks to your application. In this article, we'll cycle through building a quick API-only application uses the JSON API from Active Model Serializer. Then construct a basic CRUD Controller API example on retrieving the User information. While the process is outlined and best practices are used. The goal of the series is to make you feel confident as an engineer in building a structured project with Ruby on Rails.

Let's Begin

Let's start off by using the --api command in the console to provision an api only preset.

rails new rails-json-api-test --api

This will generate the following:

app/controllers
app/assets
app/helpers
app/models
config

Objective CRUD

Our goal is to use the ActiveModel::Serializer - JSON API - which is included by default in your Gemfile when you create an application using the --api directive. We will then work toward spinning up a basic CRUD endpoint for a user.

Below is the endpoint we plan to implement and http methods we plan to send to the API.

Path: /api/v1/users

GET
POST
PUT
DELETE

Next, we need to generate the basic User Model that we want to create using the rails generate command for Models:

First, we need to set up our database. In this example, we will be using PG (Postgres) and will set up the minimum requirement to allow us to proceed to build our API and come back to this in the next article.

gem pg

Then we need to set up our database.yml file

default: &default
  adapter: postgresql
  host: localhost
  port: 5432
  encoding: utf8
  pool: 5
  database: rails-json-api
  username: development
  password: development
development: &default
  adapter: postgresql
  host: localhost
  port: 5432
  encoding: utf8
  pool: 5
  database: rails-json-api-dev
  username: development
  password: development

Then we're ready to create the database and build our table.

rake db:create

rails g model User first_name:string last_name:string email:string

That should generate something like this:

  invoke  active_record
  create    db/migrate/20190521020122_create_users.rb
  create    app/models/user.rb
  invoke    test_unit
  create      test/models/user_test.rb
  create      test/fixtures/users.yml

Now let's work on the serializer side of things of how the API will respond to the request.

We will want to uncheck this in the gemfile:

gem 'active_model_serializers', '~> 0.10.0'

When we make the controller we will need the namespaces to be setup:

For the namespace to change fromApi::V1::UsersController to API::V1:UsersController we need to modify the inflection file which was generated in config/initializers/inflections.rb. We will then need to uncomment and add this to the file:

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym 'API'
end

Lock down your routes to ONLY ALLOW these method requests.

    Rails.application.routes.draw do
  namespace :api, path: '' do
    namespace :v1 do
      resources :users, only: [:index, :show, :create, :update, :destroy]
    end
  end
end

This is what our controller looks like to be able to support our methods:

class API::V1::UsersController < ApplicationController
  rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
  def index
    users = User.all
    render json: users, each_serializer: UserSerializer, adapter: :json_api, status: 200
  end
  def show
    user = User.find(params[:id])
    render json: user, serializer: UserSerializer, adapter: :json_api, status: 200
  end
  def create
    user = User.new(user_params)
    render json: user, serializer: UserSerializer, adapter: :json_api, status: 200 if user.save!
  end
  def update
    user = User.find(params[:id])
    render json: user, serializer: UserSerializer, adapter: :json_api, status: 200 if user.update(user_params)
  end
  def destroy
    user = User.find(params[:id])
    render json: user, serializer: UserSerializer, adapter: :json_api, status: 200 if user.destroy!
  end
  private
  def record_not_found
    render json: { message: 'Record Not Found!'}, adapter: :json_api, status: 404
  end
end

Responding with JSON

JSON API is a format that works to optimize http(s) requests mainly to promote more productivity and efficiency. JSON API comes with strong caching functionality as data changes affect fewer resources.

You can simply add this to the Serializer to implement basic key-based cache expiration:

  cache key: 'user', expires_in: 3.hours

Conclusion

We have just spun up a JSON API in a few minutes for our User Table. We have left some items for the next part of the series to strengthen our API-based application. In the next article, we will further work on building a strong JSON API and using Postgres to your advantage.

This project is available on my repo here

Additional resources

  1. Learn how to tame the Jenkins JSON API with Depth and "Tree."

  2. How do JSON and XML compare? Find out here.

  3. Discover what JSON Schema is and what role it plays.

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.