No release in over 3 years
Preconfigured setup for building JSON:API endpoints
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

Infinum JSON:API setup

Preconfigured set of libraries for building JSON:API compliant endpoints, with matchers for writing more declarative JSON:API specs. This library presumes host project is compliant with using

Installation

  1. Add Infinum JSON:API setup to your Gemfile
gem 'infinum_json_api_setup'
  1. Next, run the generator
bundle exec rails generate infinum_json_api_setup:install

The generator will copy the default translations into the host project (config/locales/json_api.en.yml), where they can be customized.

Application configuration

Create abstract class for your controllers, include common JSON:API request processing behaviour, and configure responders.

module Api
  class BaseController < ActionController::API
    include InfinumJsonApiSetup::JsonApi::ErrorHandling
    include InfinumJsonApiSetup::JsonApi::ContentNegotiation

    self.responder = InfinumJsonApiSetup::JsonApi::Responder
    respond_to :json_api
  end
end

Basic usage

Permitted parameter handling

Use jsonapi_parameters to transform incoming JSON:API compliant data into common Rails parameters

def permitted_params
  params.from_jsonapi
        .require(:user)
        .permit(:first_name, :last_name)
end

Responding

Use respond_with to initiate transformation (serialization) of domain objects into HTTP response.

def show
  respond_with User.find(params[:id])
end

respond_with is well integrated with ActiveRecord::Model interface. Given a compliant object, the method will correctly set a response status and handle object(or error) serialization based on the presence of .errors. For a successful domain operation, HTTP status will be 200 OK (or 201 in case of create controller action). Unsuccessful operations will have HTTP status 422 Unprocessable Entity with errors structured according to JSON:API specification.

def create
  respond_with User.create(permitted_params)
end

respond_with also detects usage from a destroy controller action and responds with HTTP status 204 No Content and an empty body.

def destroy
  respond_with User.destroy(params[:id])
end

Internals

This section explains the under-the-hood behavior of the library.

Content negotiation

InfinumJsonApiSetup::JsonApi::ContentNegotiation module is designed to integrate server responsibilities of content negotiation protocol described by the JSON:API specification.

Error handling

InfinumJsonApiSetup::JsonApi::ErrorHandling module is designed to catch and handle common exceptions that might bubble up when processing a request.

Exception HTTP status Bugsnag notification
ActionController::ParameterMissing 400
ActionDispatch::Http::Parameters::ParseError 400
Jure::UnpermittedSortParameters 400
I18n::InvalidLocale 400
Pundit::NotAuthorizedError 403
ActiveRecord::RecordNotFound 404
PG::Error 500

Error serialization

InfinumJsonApiSetup::JsonApi::ErrorSerializer is responsible for serializing domain errors according to JSON:API specification.

Testing

RSpec configuration

Library ships with a set of declarative matchers and request/response helpers. To use them in your specs, configure your RSpec setup in the following way which

  • includes all defined matchers
  • includes request and response helper methods into specs tagged with :request metadata
  • configures search paths for resolving JSON schema files
require 'infinum_json_api_setup/rspec'

RSpec.configure do |config|
  # Helpers
  config.include InfinumJsonApiSetup::Rspec::Helpers::RequestHelper, type: :request
  config.include InfinumJsonApiSetup::Rspec::Helpers::ResponseHelper, type: :request

  # Schema paths
  config.schema_response_root = Rails.application.root.join('path/to/response_schemas')
  config.schema_request_root = Rails.application.root.join('path/to/request_schemas')
end

Matchers

Have empty data

expect(response).to have_empty_data

Have error pointer

expect(response).to have_error_pointer('data/attributes/first_name')

Have resource count of

expect(response).to have_resource_count_of(3)

Include all resource ids

expect(response).to include_all_resource_ids(records.map(&:id))

Include all resource ids sorted

expect(response).to include_all_resource_ids_sorted(records.map(&:id))

Include all resource string ids

expect(response).to include_all_resource_string_ids(records.map(&:id).map(&:to_s))

Include error detail

expect(response).to include_error_detail('name has been taken')

Include related resource

expect(response).to include_related_resource('user', user.id)

Credits

JSON:API setup is maintained and sponsored by Infinum

License

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