Skip to content
master
Go to file
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
bin
 
 
 
 
lib
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

JsonapiForRails

A Rails 4+ plugin for providing JSONAPI v1.0 compliant APIs from your application with very little coding.

Installation

$ # Optional security step (do this once)
$ gem cert --add <(curl -Ls https://raw.githubusercontent.com/doga/jsonapi_for_rails/master/certs/doga.pem)
$
$ # Go to the root directory of your existing Rails application
$ cd path/to/railsapp
$
$ # Update the gem file
$ echo "gem 'jsonapi_for_rails'" >> Gemfile
$
$ # Install
$ # (Optional security paramater: --trust-policy MediumSecurity)
$ bundle install --trust-policy MediumSecurity
$
$ # Check the used version
$ bin/rails console
irb(main):001:0> JsonapiForRails::VERSION
=> "0.2.1"
irb(main):002:0> exit
$

Usage

1. Set up one API controller per model

Generate a controller for each model that will be accessible from your API. Controller names need to be the plural version of your model names.

$ cd path/to/railsapp
$
$ # Generate your models
$ bin/rails generate model article
$ bin/rails generate model author
$
$ # Generate your API controllers
$ bin/rails generate controller articles
$ bin/rails generate controller authors

Then enable JSONAPI in a parent class of your API controllers.

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base # or ActionController::API

  # Enable JSONAPI
  acts_as_jsonapi_resources(
    # links:               false,
    # content_negotiation: false
  )

  # ...
end

acts_as_jsonapi_resources accepts the following keyword arguments:

  • links: Setting this to false disables link generation, and speeds up your API. The default value is true.
  • content_negotiation: Setting this to false disables content negotiation. Again, this helps speed up your API, but at the expense of making your API non-JSONAPI-compliant, if only just). The default value is true.

If only some of your controllers are JSONAPI controllers, then create a parent controller for only those controllers, and enable JSONAPI inside that controller rather than ApplicationController.

$ cat > app/controllers/jsonapi_resources_controller.rb

class JsonapiResourcesController < ApplicationController
  acts_as_jsonapi_resources

  # ...
end
# app/controllers/articles_controller.rb

# Change the API controller's parent class
class ArticlesController < JsonapiResourcesController
  # ...
end

# Do the same with AuthorsController

2. Configure your API controller routes

Update your application routes as follows:

# config/routes.rb
Rails.application.routes.draw do
  # ...

  scope '/api/v1' do # Optional scoping

    [ # List your API controllers here
      :articles, :authors
    ].each do |resources_name|
      resources resources_name do
        controller resources_name do
          get     'relationships/:relationship', action: "relationship_show"
          patch   'relationships/:relationship', action: "relationship_update"
          post    'relationships/:relationship', action: "relationship_add"
          delete  'relationships/:relationship', action: "relationship_remove"
        end
      end
    end

  end

  # ...
end

3. Verify your setup

After populating your database and launching the built-in Rails server with the bin/rails server shell command, you can issue some HTTP requests to your API and verify the correctness of the responses.

$ # Get the list of articles
$ # (the returned HTTP response body is short and terse, but is prettified here for legibility)
$ curl 'http://localhost:3000/api/v1/articles'
{
  "data": [
    {
      "type": "articles",
      "id": "618037523"
    },
    {
      "type": "articles",
      "id": "994552601"
    }
  ],
  "links": {
    "self": "/api/v1/articles"
  }
}
$ # Get an article
$ curl 'http://localhost:3000/api/v1/articles/618037523'
{
  "data": {
    "type": "articles",
    "id": "618037523",
    "attributes": {
      "title": "UK bank pay and bonuses in the spotlight as results season starts",
      "content": "The pay deals handed to the bosses of Britain’s biggest banks ...",
      "created_at": "2016-03-02 14:33:49 UTC",
      "updated_at": "2016-03-02 14:33:49 UTC"
    },
    "relationships": {
      "author": {
        "data": {
          "type": "authors",
          "id": "1023487079"
        }
      }
    },
    "links": {
      "self": "/api/v1/articles/618037523"
    }
  }
}
$ # Get only the title and author of an article, include the author's name
$ curl 'http://localhost:3000/api/v1/articles/618037523?filter%5Barticles%5D=title,author;include=author;filter%5Bauthors%5D=name'
{
  "data": {
    "type": "articles",
    "id": "618037523",
    "attributes": {
      "title": "UK bank pay and bonuses in the spotlight as results season starts"
    },
    "relationships": {
      "author": {
        "data": {
          "type": "authors",
          "id": "1023487079"
        }
      }
    },
    "links": {
      "self": "/api/v1/articles/618037523"
    }
  },
  "include": [
    {
      "data": {
        "type": "authors",
        "id": "1023487079",
        "attributes": {
          "name": "Jill T..."
        },
        "relationships": {
        },
        "links": {
          "self": "/api/v1/authors/1023487079"
        }
      }
    }
  ]
}
$

Modifying the default API behaviour

By default, all API end-points are accessible to all clients, and all end-points behave the same way for all clients. In a real-world setting, you may want to restrict access to an end-point and/or change the behaviour of an end-point depending on the client.

Client authentication

Clients can be authenticated with a before_action method in your API controller. Inside controllers, instance variable names starting with the @jsonapi_ prefix and method names starting with the jsonapi_ prefix are reserved by jsonapi_for_rails, so try to avoid those.

# app/controllers/jsonapi_resources_controller.rb
class JsonapiResourcesController < ApplicationController
  acts_as_jsonapi_resources

  before_action :authenticate

private
  def authenticate
    # ...
  end
end

Access control

Access control for authenticated and unauthenticated clients can be implemented in before_action methods in your API controllers.

# app/controllers/jsonapi_resources_controller.rb
class JsonapiResourcesController < ApplicationController
  acts_as_jsonapi_resources

  before_action :permit_read, only: [
    :index,
    :show,
    :relationship_show
  ]

  before_action :permit_write, only: [
    :create, 
    :update, 
    :destroy,
    :relationship_update,
    :relationship_add,
    :relationship_remove
  ]

private
  def permit_read
    # ...
  end

  def permit_write
    # ...
  end
end

Overriding an API end-point

The bin/rails routes shell command shows you the end-points that jsonapi_for_rails defines. In order to change the behaviour of an action, you can define an action with the same name inside an API controller. jsonapi_for_rails provides utility methods and instance variables that can help you.

# app/controllers/articles_controller.rb
class ArticlesController < JsonapiResourcesController 

  def index
    # These model-related utility methods are available inside all action methods.
    jsonapi_model_class # =>  Article
    jsonapi_model_type  # => :articles

    # @jsonapi_links indicates whether links should be included in response documents.
    # It is available inside all action methods.
    @jsonapi_links      # => true

    # ...
  end

  def show
    # @jsonapi_record contains the current database record.
    # It is available inside all action methods (including all relationship
    # methods) except :index and :create.
    @jsonapi_record.to_jsonapi_hash        # => {data:   {...}}
    @jsonapi_record.to_jsonapi_errors_hash # => {errors: [...]}

    # ...
  end

  def relationship_show
    # @jsonapi_relationship is available in all relationship action methods.
    # @jsonapi_relationship[:definition] describes the current relationship.
    @jsonapi_relationship # => {:definition=>{:name=>:author, :type=>:to_one, :receiver=>{:type=>:authors, :class=>Author}}}

    # ...
  end

  def relationship_update
    # @jsonapi_relationship[:params] contains the parsed request body.
    # It is available for all relationship action methods except relationship_show.
    # @jsonapi_relationship[:params][:data] behaves like a Hash for relationships
    # of type :to_one, and as an Array for relationships of type :to_many.
    @jsonapi_relationship # => {:definition=>{...}, :params=>{"data"=>{"type"=>"authors", "id"=>"234455384"}}}

    # ...
  end

end

Implementation status

The internal architecture is sound. Test coverage is currently being bulked up using Rails 5 beta 2 (but the plugin should be compatible with Rails 4+). And missing features are being added. The intention is to release a 1.0 version around mid-2016.

Feature support roundup:

Contributing

Feel free to share your experience using this software. If you find a bug in this project, have trouble following the documentation or have a question about the project – create an issue.

License

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

About

A Rails plugin for providing JSON API compliant APIs with very little coding. http://jsonapi.org/format/ http://rubyonrails.org/

Resources

License

Packages

No packages published
You can’t perform that action at this time.