Thursday 9 February 2017

Response and exception handling in Rails 5 Api only.


We will use --api argument to tell Rails that we want an API application.

Create a rails 5 api application by running -

    $ rails new todos --api

Now, you can not that Make ApplicationController  inherit from ActionController::API  instead of ActionController::Base and it has not generated view files.

Now, generate a controller by using scaffold -

    $ rails g scaffold User first_name last_name

We will responds with JSON and an HTTP status code 200 by default. For this we will write it in concern.

In app/controllers/concerns/response.rb

    module Response
      def json_response(object, status = :ok)
        render json: object, status: status
      end
    end

Now, Suppose that we have to find a User by id. In this case where the record does not exist, ActiveRecord will throw an exception ActiveRecord::RecordNotFound. We'll rescue from this exception and return a 404 message.

Also, we will rescue ActiveRecord::RecordInvalid for the case of invalid record and it will return HTTP code 422.

So, make a module in app/controllers/concerns/exception_handler.rb -

    module ExceptionHandler
      extend ActiveSupport::Concern

      included do
        rescue_from ActiveRecord::RecordNotFound do |e|
          json_response({message: e.message}, :not_found)
        end

        rescue_from ActiveRecord::RecordInvalid do |e|
          json_response({message: e.message}, :unprocessable_entity)
        end

        rescue_from StandardError do |e|
          json_response({message: e.message}, :unprocessable_entity)
        end
      end
    end

Include these modules in the application controller.

    class ApplicationController < ActionController::API
      include Response
      include ExceptionHandler
    end

Now, in users controller we can use json_response helper for response.

    class UsersController < ApplicationController
      before_action :set_user, only: [:show, :update, :destroy]

      # GET /users
      def index
        @users = User.all
        json_response(@users)
      end

      # GET /users/1
      def show
        json_response(@user)
      end

      # POST /users
      def create
        @user = User.create!(user_params)
        json_response(@user, :created)
      end

      # PATCH/PUT /users/1
      def update
        @user.update(user_params)
        head :no_content
      end

      # DELETE /users/1
      def destroy
        @user.destroy
        head :no_content
      end

      private
      # Use callbacks to share common setup or constraints between actions.
      def set_user
        @user = User.find(params[:id])
      end

      # Only allow a trusted parameter "white list" through.
      def user_params
        params.permit(:first_name, :last_name)
      end
    end

Note that we are using create! instead of create because as we know that create! raise a exception.

You can customize the error message like this -

    def create
      user = User.new(user_params)
      if user.save
        json_response(user)
      else
            json_response({error: 'User has not created'}, :unprocessable_entity)
      end
    end



1 comment:

  1. I've created this gem that uses a similar approach to handle errors for you:
    https://github.com/jamesstonehill/api_error_handler

    ReplyDelete