jsonapi_actions

Implement Rails JSONAPI compliant controller actions.


License
MIT
Install
gem install jsonapi_actions -v 0.1.0

Documentation

JsonapiActions

Instantly create flexible API controllers that are compatible with JSON:API. Utilize your existing FastJsonapi or ActiveModel::Serializer serialization library, or bring your own. Scope and authenticate with optional Pundit policies and/or Controller specific methods.

Installation

Add this line to your application's Gemfile:

gem 'jsonapi_actions'

And then execute:

$ bundle install

Usage

Basic Setup

Include the JsonapiActions::ErrorHandling and JsonapiActions::ErrorHandling modules in your base controller.

# app/controllers/api/v1/base_controller.rb
module Api::V1
  class BaseController < ApplicationController
    include JsonApiActions
    
    respond_to :json
  end
end

Define the Model for each child Controller.

# app/controllers/api/v1/projects_controller.rb
module Api::V1
  class ProjectsController < BaseController
    self.model = Project
  end 
end

Define your routes.

# config/routes.rb
Rails.application.routes.draw do
  namespace :api, defaults: { format: 'json' } do
    namespace :v1 do
      resources :projects
    end
  end
end

parent_scope

Index actions can be scoped by parent associations. i.e. nested routes.

# app/models/project.rb
class Project < ApplicationRecord
  belongs_to :user 
end

# config/routes.rb
Rails.application.routes.draw do
  namespace :api, defaults: { format: 'json' } do
    namespace :v1 do
      # /api/v1/projects
      resources :projects
      
      resources :users do
        # /api/v1/users/:user_id/projects
        resources :projects
      end
    end
  end
end

# app/controllers/api/v1/projects_controller.rb
module Api::V1
  class ProjectsController < BaseController
    self.model = Project
    self.parent_associations = [
      # When Using using attribute on the model
      #   Project.where(user_id: params[:user_id]) 
      { param: :user_id, attribute: :user_id }, 
      
      # When using attribute on the associated model
      #   Project.joins(:user).where(users: { id: params[:user_id] }) 
      { param: :user_id, association: :user, table: :users, attribute: :id } 
    ]
  end 
end

Custom Actions (Non-CRUD)

You can easily utilize JsonapiActions in custom controller actions too. Just call #set_record to initialize @record and when you're done render response(@record)

# app/controllers/api/v1/projects_controller.rb
module Api::V1
  class ProjectsController < BaseController
    self.model = Project
    
    def activate
      set_record
      @record.activate!
      render response(@record)
    rescue ActiveRecord::RecordInvalid
      render unprocessable_entity(@record)
    end
  end 
end

#serializer

Controller actions render JSON data via a Serializer. We assume a Model has a ModelSerializer. To use a different serializer, define self.serializer = OtherSerializer on the Controller.

# app/controllers/api/v1/base_controller.rb
module Api::V1
  class ProjectsController < BaseController
    self.model = Project
    self.serializer = SecretProjectSerializer
  end 
end

#json_response

Response data is formatted so that it can be rendered with FastJsonapi or ActiveModel::Serializer. If you are using a different serializer, or would like to further change the response. Then you will need to override #response, which defines the arguments for render.

# app/controllers/api/v1/base_controller.rb
module Api::V1
 class BaseController < ApplicationController
   include JsonapiActions
   
   respond_to :json
   
   private
    
      def json_response(data, options = {})
        { json: data }.merge(options)
      end
 end
end

Pundit

JsonapiActions are built to use Pundit for authorization. We utilize action authorization, policy scope, and permitted params.

# app/policies/project_policy.rb
class ProjectPolicy < ApplicationPolicy
  def index?
    true
  end
  
  def show?
    record.user == user
  end
   
  def create?
    record.user == user
  end
    
  def update?
    record.user == user  
  end
    
  def destroy?
    record.user == user  
  end
  
  def permitted_params
    %i[user_id name]
  end
  
  class Scope < Scope
    def resolve
      scope.where(user_id: user.id)
    end
  end
end

Usage without Pundit

If you are not using Pundit for authorization, then you will need to defined #permitted_params.
You can optionally override methods for #policy_scope and #authorize too.

module Api::V1
  class BaseController < ApplicationController
    include JsonapiActions::ErrorHandling
    
    respond_to :json
    
    private
    
      def permitted_params
        %i[user_id name]
      end
      
      # This override is optional
      def policy_scope(scope)
        scope.where(user_id: current_user.id)
      end
     
      # This override is optional
      def authorize(record, query = nil)
        return if record.user == current_user
        
        raise NotAuthorized
      end
  end
end

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/jsonapi_actions. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the JsonapiActions project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.