{"id":13482844,"url":"https://github.com/tiagopog/jsonapi-utils","last_synced_at":"2025-08-04T06:39:58.271Z","repository":{"id":47077260,"uuid":"42141791","full_name":"tiagopog/jsonapi-utils","owner":"tiagopog","description":"Build JSON API-compliant APIs on Rails with no (or less) learning curve.","archived":false,"fork":false,"pushed_at":"2022-04-30T11:37:56.000Z","size":404,"stargazers_count":215,"open_issues_count":25,"forks_count":81,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-07-28T00:03:13.407Z","etag":null,"topics":["activerecord","api","json","json-api","jsonapi","rails","resource","rest","ruby","serializer","utils"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tiagopog.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-09-08T22:12:09.000Z","updated_at":"2024-12-12T11:35:41.000Z","dependencies_parsed_at":"2022-08-27T22:00:16.401Z","dependency_job_id":null,"html_url":"https://github.com/tiagopog/jsonapi-utils","commit_stats":null,"previous_names":["b2beauty/jsonapi-utils","b2beauty/jsonapi_utils"],"tags_count":37,"template":false,"template_full_name":null,"purl":"pkg:github/tiagopog/jsonapi-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiagopog%2Fjsonapi-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiagopog%2Fjsonapi-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiagopog%2Fjsonapi-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiagopog%2Fjsonapi-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tiagopog","download_url":"https://codeload.github.com/tiagopog/jsonapi-utils/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiagopog%2Fjsonapi-utils/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268658673,"owners_count":24285737,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-04T02:00:09.867Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activerecord","api","json","json-api","jsonapi","rails","resource","rest","ruby","serializer","utils"],"created_at":"2024-07-31T17:01:06.009Z","updated_at":"2025-08-04T06:39:58.202Z","avatar_url":"https://github.com/tiagopog.png","language":"Ruby","readme":"# JSONAPI::Utils\n\n[![Code Climate](https://codeclimate.com/github/tiagopog/jsonapi-utils/badges/gpa.svg)](https://codeclimate.com/github/tiagopog/jsonapi-utils)\n[![Gem Version](https://badge.fury.io/rb/jsonapi-utils.svg)](https://badge.fury.io/rb/jsonapi-utils)\n[![Build Status](https://travis-ci.org/tiagopog/jsonapi-utils.svg?branch=master)](https://travis-ci.org/tiagopog/jsonapi-utils)\n\nSimple yet powerful way to get your Rails API compliant with [JSON API](http://jsonapi.org).\n\n`JSONAPI::Utils` (JU) is built on top of [JSONAPI::Resources](https://github.com/cerebris/jsonapi-resources)\ntaking advantage of its resource-driven style and bringing a set of helpers to easily build modern JSON APIs \nwith no or less learning curve.\n\nAfter installing the gem and defining the resources/routes, it's as simple as calling a render helper:\n\n```ruby\nclass UsersController \u003c ActionController::Base\n  include JSONAPI::Utils\n\n  def index\n    jsonapi_render json: User.all\n  end\nend\n```\n\n## Table of Contents\n\n* [Installation](#installation)\n* [Why JSONAPI::Utils?](#why-jsonapiutils)\n* [Usage](#usage)\n  * [Response](#response)\n    * [Renders](#renders)\n    * [Formatters](#formatters)\n    * [Paginators](#paginators)\n  * [Request](#request)\n    * [Params helpers](#params-helpers)\n* [Full example](#full-example)\n  * [Models](#models)\n  * [Resources](#resources)\n  * [Routes \u0026 Controllers](#routes--controllers)\n  * [Initializer](#initializer)\n  * [Requests \u0026 Responses](#requests--responses)\n    * [Index](#index)\n    * [Index (options)](#index-options)\n    * [Show](#show)\n    * [Show (options)](#show-options)\n    * [Relationships (identifier objects)](#relationships-identifier-objects)\n    * [Nested resources](#nested-resources)\n* [Development](#development)\n* [Contributing](#contributing)\n* [License](#license)\n\n## Installation\n\nSupport:\n\n* Ruby 1.9+ with Rails 4\n* Ruby 2.4+ with Rails 5\n\nFor Rails 4 add this to your application's Gemfile:\n\n```ruby\ngem 'jsonapi-utils', '~\u003e 0.4.9'\n```\n\nFor Rails 5+:\n\n```ruby\ngem 'jsonapi-utils', '~\u003e 0.7.3'\n```\n\nAnd then execute:\n\n```shell\n$ bundle\n```\n\n## Why JSONAPI::Utils?\n\nOne of the main motivations behind `JSONAPI::Utils` is to keep things explicit in controllers (no hidden actions :-) so that developers can easily understand and maintain their code. \n\nUnlike `JSONAPI::Resources` (JR), JU doesn't care about how you will operate your controller actions. The gem deals only with the request validation and response rendering (via JR's objects) and provides a set of helpers (renders, formatters etc) along the way. Thus developers can decide how to actually operate their actions: service objects, interactors etc.\n\n## Usage\n\n### Response\n\n#### Renders\n\nJU brings two main renders to the game, working pretty much the same way as Rails' `ActionController#render` method:\n\n- jsonapi_render\n- jsonapi_render_errors\n\n**jsonapi_render**\n\nIt renders a JSON API-compliant response.\n\n```ruby\n# app/controllers/users_controller.rb\n# GET /users\ndef index\n  jsonapi_render json: User.all\nend\n\n# GET /users/:id\ndef show\n  jsonapi_render json: User.find(params[:id])\nend\n```\n\nArguments:\n\n  - `json`: object to be rendered as a JSON document: ActiveRecord object, Hash or Array\u003cHash\u003e;\n  - `status`: HTTP status code (Integer, String or Symbol). If ommited a status code will be automatically infered;\n  - `options`:\n    - `resource`: explicitly points the resource to be used in the serialization. By default, JU will select resources by inferencing from controller's name.\n    - `count`: explicitly points the total count of records for the request in order to build a proper pagination. By default, JU will count the total number of records.\n    - `model`: sets the model reference in cases when `json` is a Hash or a collection of Hashes.\n\nOther examples:\n\n```ruby\n# Specify a particular HTTP status code\njsonapi_render json: new_user, status: :created\n\n# Forcing a different resource\njsonapi_render json: User.all, options: { resource: V2::UserResource }\n\n# Using a specific count\njsonapi_render json: User.some_weird_scope, options: { count: User.some_weird_scope_count }\n\n# Hash rendering\njsonapi_render json: { data: { id: 1, first_name: 'Tiago' } }, options: { model: User }\n\n# Collection of Hashes rendering\njsonapi_render json: { data: [{ id: 1, first_name: 'Tiago' }, { id: 2, first_name: 'Doug' }] }, options: { model: User }\n```\n\n**jsonapi_render_errors**\n\nIt renders a JSON API-compliant error response.\n\n```ruby\n# app/controllers/users_controller.rb\n# POST /users\n  def create\n    user = User.new(user_params)\n    if user.save\n      jsonapi_render json: user, status: :created\n    else\n      jsonapi_render_errors json: user, status: :unprocessable_entity\n    end\n  end\n```\n\nArguments:\n  - Exception\n  - `json`: object to be rendered as a JSON document: ActiveRecord, Exception, Array\u003cHash\u003e or any object which implements the `errors` method;\n  - `status`: HTTP status code (Integer, String or Symbol). If ommited a status code will be automatically infered from the error body.\n\nOther examples:\n\n```ruby\n# Render errors from a custom exception:\njsonapi_render_errors Exceptions::MyCustomError.new(user)\n\n# Render errors from an Array\u003cHash\u003e:\nerrors = [{ id: 'validation', title: 'Something went wrong', code: '100' }]\njsonapi_render_errors json: errors, status: :unprocessable_entity\n```\n\n#### Formatters\n\nIn the backstage these are the guys which actually parse the ActiveRecord/Hash object to build a new Hash compliant with JSON API's specs. Formatters can be called anywhere in controllers being very useful if you need to do some work with the response's body before rendering the actual response.\n\n\u003e Note: the resulting Hash from those methods can not be passed as argument to `JSONAPI::Utils#jsonapi_render` or  `JSONAPI::Utils#jsonapi_render_error`, instead it needs to be rendered by the usual `ActionController#render`.\n\n**jsonapi_format**\n\n\u003e Because of semantic reasons `JSONAPI::Utils#jsonapi_serialize` was renamed to `JSONAPI::Utils#jsonapi_format`.\n\n```ruby\n# app/controllers/users_controller.rb\ndef index\n  body = jsonapi_format(User.all)\n  render json: do_some_magic_with(body)\nend\n```\n\nArguments:\n  - First: ActiveRecord object, Hash or Array\u003cHash\u003e;\n  - Last: Hash of options (same as `JSONAPI::Utils#jsonapi_render`).\n\n#### Paginators\n\nPagination works out of the box on JU, you just need to decide which kind of paginator you'd like to use.\n\nIt's really easy to work with pagination on JU, actually it's just a matter of chosing the [paginator you wish](http://jsonapi-resources.com/v0.8/guide/configuration.html#Defaults) in your JR's config file:\n\n```ruby\n# config/initializers/jsonapi_resources.rb\nJSONAPI.configure do |config|\n  # :none, :offset, :paged, or a custom paginator name\n  config.default_paginator = :paged\n\n  # Output pagination links at top level\n  config.top_level_links_include_pagination = true\n  \n  # Default sizes\n  config.default_page_size = 70\n  config.maximum_page_size = 100\nend\n```\n\nAs you may have noticed above, it's possible to use custom paginators. In order to create your own paginator your just need to define a class which inherits from `JSONAPI::Paginator` and implements the `#pagination_range` method which in turn must return the range to be applied over the resulting collection.\n\nFor example, if you would like to paginate over a collection of hashes, you may implement the `#pagination_range` method as below:\n\n```ruby\nclass CustomPaginator \u003c JSONAPI::Paginator\n  def pagination_range(page_params)\n    offset = page_params['offset']\n    limit  = JSONAPI.configuration.default_page_size\n    offset..offset + limit - 1 # resulting range\n  end\n```\n\nAnd then it can be either set at the resource class level (e.g. UserResource.paginator :custom) or via config initializer:\n\n```ruby\n# config/initializers/jsonapi_resources.rb\nJSONAPI.configure do |config|\n  config.default_paginator = :custom\nend\n```\n\n### Request\n\nBefore a controller action gets executed, `JSONAPI::Utils` will validate the request against JSON API's specs as well as evaluating the eventual query string params to check if they match the resource's definition. If something goes wrong during the validation process, JU will render an error response like this examples below:\n\n```json\nHTTP/1.1 400 Bad Request\nContent-Type: application/vnd.api+json\n\n{\n  \"errors\": [\n    {\n      \"title\": \"Invalid resource\",\n      \"detail\": \"foo is not a valid resource.\",\n      \"code\": \"101\",\n      \"status\": \"400\"\n    },\n    {\n      \"title\": \"Invalid resource\",\n      \"detail\": \"foobar is not a valid resource.\",\n      \"code\": \"101\",\n      \"status\": \"400\"\n    },\n    {\n      \"title\": \"Invalid field\",\n      \"detail\": \"bar is not a valid relationship of users\",\n      \"code\": \"112\",\n      \"status\": \"400\"\n    }\n  ]\n}\n```\n\n#### Params helpers\n\nJU brings helper methods as a shortcut to get values from permitted params based on the resource's configuration.\n\n- `resource_params`:\n  - Returns the permitted params present in the `attributes` JSON member;\n    - Example: `{ name: 'Bilbo', gender: 'male', city: 'Shire' }`\n  - Same of calling: `params.require(:data).require(:attributes).permit(:name, :gender, :city)`\n- `relationship_params`:\n  - Returns the relationship `id`s, distinguished by key, present in `relationships` JSON member;\n    - Example: `{ author: 1, posts: [1, 2, 3] }`\n  - Same as calling: `params.require(:relationships).require(:author).require(:data).permit(:id)`\n\n## Full example\n\nAfter installing the gem you simply need to:\n\n1. Include the gem's module (`include JSONAPI::Utils`) in a controller (eg. `BaseController`);\n2. Define the resources which will be exposed via REST API;\n3. Define the application's routes;\n4. Use JSONAPI Utils' helper methods (eg. renders, formatters, params helpers etc).\n\nOk, now it's time to start our complete example. So, let's say we have a Rails application for a super simple blog:\n\n### Models\n\n```ruby\n# app/models/user.rb\nclass User \u003c ActiveRecord::Base\n  has_many :posts\n  validates :first_name, :last_name, presence: true\nend\n\n# app/models/post.rb\nclass Post \u003c ActiveRecord::Base\n  belongs_to :author, class_name: 'User', foreign_key: 'user_id'\n  validates :title, :body, presence: true\nend\n```\n\n### Resources\n\nHere is where we define how our models are exposed as resources on the API:\n\n```ruby\n# app/resources/user_resource.rb\nclass UserResource \u003c JSONAPI::Resource\n  attributes :first_name, :last_name, :full_name, :birthday\n\n  has_many :posts\n\n  def full_name\n    \"#{@model.first_name} #{@model.last_name}\"\n  end\nend\n\n# app/resources/post_resource.rb\nclass PostResource \u003c JSONAPI::Resource\n  attributes :title, :body\n  has_one :author\nend\n```\n\n### Routes \u0026 Controllers\n\nLet's define the routes using the `jsonapi_resources` method provided by JR:\n\n```ruby\nRails.application.routes.draw do\n  jsonapi_resources :users do\n    jsonapi_resources :posts\n  end\nend\n```\n\nIn controllers we just need to include the `JSONAPI::Utils` module.\n\n\u003e Note: some default rendering can be set like the below example where `jsonapi_render_not_found` is used when a record is not found in the database.\n\n```ruby\n# app/controllers/base_controller.rb\nclass BaseController \u003c ActionController::Base\n  include JSONAPI::Utils\n  protect_from_forgery with: :null_session\n  rescue_from ActiveRecord::RecordNotFound, with: :jsonapi_render_not_found\nend\n```\n\nWith the helper methods inhirited from `JSONAPI::Utils` in our `BaseController`, now it's all about to write our actions like the following:\n\n```ruby\n# app/controllers/users_controller.rb\nclass UsersController \u003c BaseController\n  # GET /users\n  def index\n    users = User.all\n    jsonapi_render json: users\n  end\n\n  # GET /users/:id\n  def show\n    user = User.find(params[:id])\n    jsonapi_render json: user\n  end\n\n  # POST /users\n  def create\n    user = User.new(resource_params)\n    if user.save\n      jsonapi_render json: user, status: :created\n    else\n      jsonapi_render_errors json: user, status: :unprocessable_entity\n    end\n  end\n\n  # PATCH /users/:id\n  def update\n    user = User.find(params[:id])\n    if user.update(resource_params)\n      jsonapi_render json: user\n    else\n      jsonapi_render_errors json: user, status: :unprocessable_entity\n    end\n  end\n\n  # DELETE /users/:id\n  def destroy\n    User.find(params[:id]).destroy\n    head :no_content\n  end\nend\n```\n\nAnd:\n\n```ruby\n# app/controllers/posts_controller.rb\nclass PostsController \u003c BaseController\n  before_action :load_user, except: :create\n\n  # GET /users/:user_id/posts\n  def index\n    jsonapi_render json: @user.posts, options: { count: 100 }\n  end\n\n  # GET /users/:user_id/posts/:id\n  def show\n    jsonapi_render json: @user.posts.find(params[:id])\n  end\n\n  # POST /posts\n  def create\n    post = Post.new(post_params)\n    if post.save\n      jsonapi_render json: post, status: :created\n    else\n      jsonapi_render_errors json: post, status: :unprocessable_entity\n    end\n  end\n\n  private\n\n  def post_params\n    resource_params.merge(user_id: relationship_params[:author])\n  end\n\n  def load_user\n    @user = User.find(params[:user_id])\n  end\nend\n```\n\n### Initializer\n\nIn order to enable a proper pagination, record count etc, an initializer could be defined such as:\n\n```ruby\n# config/initializers/jsonapi_resources.rb\nJSONAPI.configure do |config|\n  config.json_key_format = :underscored_key\n  config.route_format = :dasherized_route\n\n  config.allow_include = true\n  config.allow_sort = true\n  config.allow_filter = true\n\n  config.raise_if_parameters_not_allowed = true\n\n  config.default_paginator = :paged\n\n  config.top_level_links_include_pagination = true\n\n  config.default_page_size = 10\n  config.maximum_page_size = 20\n\n  config.top_level_meta_include_record_count = true\n  config.top_level_meta_record_count_key = :record_count\n\n  config.top_level_meta_include_page_count = true\n  config.top_level_meta_page_count_key = :page_count\n\n  config.use_text_errors = false\n\n  config.exception_class_whitelist = []\n\n  config.always_include_to_one_linkage_data = false\nend\n```\n\nYou may want a different configuration for your API. For more information check [this](https://github.com/cerebris/jsonapi-resources/#configuration).\n\n### Requests \u0026 Responses\n\nHere are examples of requests – based on those sample [controllers](#routes--controllers) – and their respective JSON responses.\n\n* [Collection](#collection)\n* [Collection (options)](#collection-options)\n* [Single record](#single-record)\n* [Record (options)](#single-record-options)\n* [Relationships (identifier objects)](#relationships-identifier-objects)\n* [Nested resources](#nested-resources)\n\n#### Index\n\nRequest:\n\n```\nGET /users HTTP/1.1\nAccept: application/vnd.api+json\n```\n\nResponse:\n\n```json\nHTTP/1.1 200 OK\nContent-Type: application/vnd.api+json\n\n{\n  \"data\": [\n    {\n      \"id\": \"1\",\n      \"type\": \"users\",\n      \"links\": {\n        \"self\": \"http://api.myblog.com/users/1\"\n      },\n      \"attributes\": {\n        \"first_name\": \"Tiago\",\n        \"last_name\": \"Guedes\",\n        \"full_name\": \"Tiago Guedes\",\n        \"birthday\": null\n      },\n      \"relationships\": {\n        \"posts\": {\n          \"links\": {\n            \"self\": \"http://api.myblog.com/users/1/relationships/posts\",\n            \"related\": \"http://api.myblog.com/users/1/posts\"\n          }\n        }\n      }\n    },\n    {\n      \"id\": \"2\",\n      \"type\": \"users\",\n      \"links\": {\n        \"self\": \"http://api.myblog.com/users/2\"\n      },\n      \"attributes\": {\n        \"first_name\": \"Douglas\",\n        \"last_name\": \"André\",\n        \"full_name\": \"Douglas André\",\n        \"birthday\": null\n      },\n      \"relationships\": {\n        \"posts\": {\n          \"links\": {\n            \"self\": \"http://api.myblog.com/users/2/relationships/posts\",\n            \"related\": \"http://api.myblog.com/users/2/posts\"\n          }\n        }\n      }\n    }\n  ],\n  \"meta\": {\n    \"record_count\": 2\n  },\n  \"links\": {\n    \"first\": \"http://api.myblog.com/users?page%5Bnumber%5D=1\u0026page%5Bsize%5D=10\",\n    \"last\": \"http://api.myblog.com/users?page%5Bnumber%5D=1\u0026page%5Bsize%5D=10\"\n  }\n}\n```\n\n#### Index (options)\n\nRequest:\n\n```\nGET /users?include=posts\u0026fields[users]=first_name,last_name,posts\u0026fields[posts]=title\u0026sort=first_name,last_name\u0026page[number]=1\u0026page[size]=1 HTTP/1.1\nAccept: application/vnd.api+json\n```\n\nResponse:\n\n```json\nHTTP/1.1 200 OK\nContent-Type: application/vnd.api+json\n\n{\n  \"data\": [\n    {\n      \"id\": \"2\",\n      \"type\": \"users\",\n      \"links\": {\n        \"self\": \"http://api.myblog.com/users/2\"\n      },\n      \"attributes\": {\n        \"first_name\": \"Douglas\",\n        \"last_name\": \"André\"\n      },\n      \"relationships\": {\n        \"posts\": {\n          \"links\": {\n            \"self\": \"http://api.myblog.com/users/2/relationships/posts\",\n            \"related\": \"http://api.myblog.com/users/2/posts\"\n          },\n          \"data\": []\n        }\n      }\n    },\n    {\n      \"id\": \"1\",\n      \"type\": \"users\",\n      \"links\": {\n        \"self\": \"http://api.myblog.com/users/1\"\n      },\n      \"attributes\": {\n        \"first_name\": \"Tiago\",\n        \"last_name\": \"Guedes\"\n      },\n      \"relationships\": {\n        \"posts\": {\n          \"links\": {\n            \"self\": \"http://api.myblog.com/users/1/relationships/posts\",\n            \"related\": \"http://api.myblog.com/users/1/posts\"\n          },\n          \"data\": [\n            {\n              \"type\": \"posts\",\n              \"id\": \"1\"\n            }\n          ]\n        }\n      }\n    }\n  ],\n  \"included\": [\n    {\n      \"id\": \"1\",\n      \"type\": \"posts\",\n      \"links\": {\n        \"self\": \"http://api.myblog.com/posts/1\"\n      },\n      \"attributes\": {\n        \"title\": \"An awesome post\"\n      }\n    }\n  ],\n  \"meta\": {\n    \"record_count\": 2\n  },\n  \"links\": {\n    \"first\": \"http://api.myblog.com/users?fields%5Bposts%5D=title\u0026fields%5Busers%5D=first_name%2Clast_name%2Cposts\u0026include=posts\u0026page%5Blimit%5D=2\u0026page%5Boffset%5D=0\u0026sort=first_name%2Clast_name\",\n    \"last\": \"http://api.myblog.com/users?fields%5Bposts%5D=title\u0026fields%5Busers%5D=first_name%2Clast_name%2Cposts\u0026include=posts\u0026page%5Blimit%5D=2\u0026page%5Boffset%5D=0\u0026sort=first_name%2Clast_name\"\n  }\n}\n```\n\n#### Show\n\nRequest:\n\n```\nGET /users/1 HTTP/1.1\nAccept: application/vnd.api+json\n```\n\nResponse:\n\n```json\nHTTP/1.1 200 OK\nContent-Type: application/vnd.api+json\n\n{\n  \"data\": {\n    \"id\": \"1\",\n    \"type\": \"users\",\n    \"links\": {\n      \"self\": \"http://api.myblog.com/users/1\"\n    },\n    \"attributes\": {\n      \"first_name\": \"Tiago\",\n      \"last_name\": \"Guedes\",\n      \"full_name\": \"Tiago Guedes\",\n      \"birthday\": null\n    },\n    \"relationships\": {\n      \"posts\": {\n        \"links\": {\n          \"self\": \"http://api.myblog.com/users/1/relationships/posts\",\n          \"related\": \"http://api.myblog.com/users/1/posts\"\n        }\n      }\n    }\n  }\n}\n```\n\n#### Show (options)\n\nRequest:\n\n```\nGET /users/1?include=posts\u0026fields[users]=full_name,posts\u0026fields[posts]=title HTTP/1.1\nAccept: application/vnd.api+json\n```\n\nResponse:\n\n```json\nHTTP/1.1 200 OK\nContent-Type: application/vnd.api+json\n\n{\n  \"data\": {\n    \"id\": \"1\",\n    \"type\": \"users\",\n    \"links\": {\n      \"self\": \"http://api.myblog.com/users/1\"\n    },\n    \"attributes\": {\n      \"full_name\": \"Tiago Guedes\"\n    },\n    \"relationships\": {\n      \"posts\": {\n        \"links\": {\n          \"self\": \"http://api.myblog.com/users/1/relationships/posts\",\n          \"related\": \"http://api.myblog.com/users/1/posts\"\n        },\n        \"data\": [\n          {\n            \"type\": \"posts\",\n            \"id\": \"1\"\n          }\n        ]\n      }\n    }\n  },\n  \"included\": [\n    {\n      \"id\": \"1\",\n      \"type\": \"posts\",\n      \"links\": {\n        \"self\": \"http://api.myblog.com/posts/1\"\n      },\n      \"attributes\": {\n        \"title\": \"An awesome post\"\n      }\n    }\n  ]\n}\n```\n\n#### Relationships (identifier objects)\n\nRequest:\n\n```\nGET /users/1/relationships/posts HTTP/1.1\nAccept: application/vnd.api+json\n```\n\nResponse:\n\n```json\nHTTP/1.1 200 OK\nContent-Type: application/vnd.api+json\n\n{\n  \"links\": {\n    \"self\": \"http://api.myblog.com/users/1/relationships/posts\",\n    \"related\": \"http://api.myblog.com/users/1/posts\"\n  },\n  \"data\": [\n    {\n      \"type\": \"posts\",\n      \"id\": \"1\"\n    }\n  ]\n}\n```\n\n#### Nested resources\n\nRequest:\n\n```\nGET /users/1/posts HTTP/1.1\nAccept: application/vnd.api+json\n```\n\nResponse:\n\n```json\nHTTP/1.1 200 OK\nContent-Type: application/vnd.api+json\n\n{\n  \"data\": [\n    {\n      \"id\": \"1\",\n      \"type\": \"posts\",\n      \"links\": {\n        \"self\": \"http://api.myblog.com/posts/1\"\n      },\n      \"attributes\": {\n        \"title\": \"An awesome post\",\n        \"body\": \"Lorem ipsum dolot sit amet\"\n      },\n      \"relationships\": {\n        \"author\": {\n          \"links\": {\n            \"self\": \"http://api.myblog.com/posts/1/relationships/author\",\n            \"related\": \"http://api.myblog.com/posts/1/author\"\n          }\n        }\n      }\n    }\n  ],\n  \"meta\": {\n    \"record_count\": 1\n  },\n  \"links\": {\n    \"first\": \"http://api.myblog.com/posts?page%5Bnumber%5D=1\u0026page%5Bsize%5D=10\",\n    \"last\": \"http://api.myblog.com/posts?page%5Bnumber%5D=1\u0026page%5Bsize%5D=10\"\n  }\n}\n```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo 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](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/tiagopog/jsonapi-utils. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://contributor-covenant.org) code of conduct.\n\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n\n\n","funding_links":[],"categories":["Ruby","API Builder and Discovery"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiagopog%2Fjsonapi-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftiagopog%2Fjsonapi-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiagopog%2Fjsonapi-utils/lists"}