{"id":15152714,"url":"https://github.com/ed-mare/json_api_server","last_synced_at":"2026-02-11T21:33:39.294Z","repository":{"id":56879503,"uuid":"103325657","full_name":"ed-mare/json_api_server","owner":"ed-mare","description":"Ruby gem implements JSON API 1.0 - data, errors, meta, pagination, sort, filters, sparse fieldsets and inclusion of related resources. For creating APIs, not consuming. ","archived":false,"fork":false,"pushed_at":"2018-03-28T19:34:33.000Z","size":129,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-12-04T05:17:08.661Z","etag":null,"topics":["json-api","rails","ruby"],"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/ed-mare.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":"2017-09-12T22:15:50.000Z","updated_at":"2018-03-28T19:34:34.000Z","dependencies_parsed_at":"2022-08-20T11:40:40.901Z","dependency_job_id":null,"html_url":"https://github.com/ed-mare/json_api_server","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ed-mare/json_api_server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ed-mare%2Fjson_api_server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ed-mare%2Fjson_api_server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ed-mare%2Fjson_api_server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ed-mare%2Fjson_api_server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ed-mare","download_url":"https://codeload.github.com/ed-mare/json_api_server/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ed-mare%2Fjson_api_server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29345523,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-11T20:11:40.865Z","status":"ssl_error","status_checked_at":"2026-02-11T20:10:41.637Z","response_time":97,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["json-api","rails","ruby"],"created_at":"2024-09-26T16:21:42.156Z","updated_at":"2026-02-11T21:33:39.269Z","avatar_url":"https://github.com/ed-mare.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JsonApiServer\n\nThis gem provides tools for building JSON APIs per http://jsonapi.org spec (1.0).\nIt supports sparse fieldsets, sorting, filtering, pagination, and inclusion of\nrelated resources. Sorting, filtering, and inclusions are whitelisted\nso depth/complexity can be controlled.\n\nThis library: (1) generates data for sorting, filtering,\ninclusions, and pagination, (2) provides serializers and helper classes for\nbuilding the response with this data, and (3) handles/renders errors in JSON API format.\nOtherwise, you build your API as you normally do (i.e, routes, authorization,\ncaching, etc.).\n\nUse at your own risk. This gem is under development and has not been used\nin production yet. Known to work with Ruby 2.3.x and Rails 5.x.\nSupports only ActiveRecord at this time.\n\n- **Example app** -\u003e https://github.com/ed-mare/jsonapiserver-example\n- **Documentation** -\u003e Install gem rdoc for more documentation.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'json_api_server'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install json_api_server\n\n## Usage\n\nInstall gem rdoc for more documentation.\n\n##### 1) Include JsonApiServer::Controller::ErrorHandling in your base API controller.\n\n```ruby\n# i.e.,\nclass BaseApiController \u003c ApplicationController\n  include JsonApiServer::Controller::ErrorHandling\nend\n```\n\nIt provides methods which render errors per http://jsonapi.org/format/#errors:\n\n- `render_400`, `render_401`, `render_404`, `render_409`, `render_422`, `render_500`,\n`render_unknown_format`, `render_503`.\n\nIt rescues from and renders JSON API errors for:\n\n- StandardError (render_500)\n- JsonApiServer::BadRequest (render_400)\n- ActionController::BadRequest (render_400)\n- ActiveRecord::RecordNotFound, ActionController::RoutingError (render_404)\n- ActiveRecord::RecordNotUnique (render_409)\n- ActionController::RoutingError (render_404)\n- ActionController::UrlGenerationError (render_404)\n- ActionController::UnknownController (render_404)\n- ActionController::UnknownFormat (render_unknown_format 406)\n\nAdditionally:\n\n- Use `render_422(object)` in controllers to render validation errors.\n\nError messages are defined in 'config/locales/en.yml' and can be customized\nor redefined.\n\ni.e.,\n\n```ruby\n# Given a sandwiches controller...\nclass Api::V1::SandwichesController \u003c Api::BaseController\n    ...\nend\n\n# config/locales/en.yml\nen:\n  json_api_server:\n    controller:\n      sandwiches:\n        name: 'sandwich'\n\n# messages now become:\n# 404 =\u003e \"This sandwich does not exist.\"\n# 409 =\u003e \"This sandwich already exists.\"\n```\n\n##### 2) Include JsonApiServer::MimeTypes in initializers\n\nSpec: \"JSON API requires use of the JSON API media type\n(application/vnd.api+json) for exchanging data.\"\n\nTo support this mime type:\n\n```ruby\n# config/initializers/mime_types.rb\ninclude JsonApiServer::MimeTypes\n```\n##### 3) Configure the gem\n\nConfigure the logger, base URL, serialization options and filter builders.\n\n```ruby\n# config/initializers/json_api_server.rb\nJsonApiServer.configure do |c|\n  c.base_url = 'https://www.something.com'\n  c.logger = Rails.logger\n  c.serializer_options = {\n        escape_mode: :json,\n        time: :xmlschema,\n        mode: :compat\n      }\nend\n```\n\nOptionally, set this Rails config so '`\u0026`' is not escaped in pagination URLs:\n\n```ruby\n# application.rb\nconfig.active_support.escape_html_entities_in_json = false\n```\n\n##### 4) Use JsonApiServer::Builder to collect data in the controller.\n\nThis class handles pagination, sorting, filters, inclusion of\nrelated resources, and sparse fieldsets. It collects the necessary data for the\nserializers.\n\nThis class uses the following classes. See each class's rdoc for configuration options.\n\n- JsonApiServer::Pagination\n- JsonApiServer::Sort\n- JsonApiServer::Filter\n- JsonApiServer::Include\n- JsonApiServer::Fields\n\nA variety of search capabilities can be configured for model attributes with\n`JsonApiServer::Filter` - LIKE queries, ILIKE queries (Postgres), IN queries,\ncomparison queries, range queries, and queries against Postgres jsonb arrays\n(case sensitive/insensitive). Plus, custom queries and custom filters can be easily\nadded.\n\n**Example**:\n\n```ruby\nclass TopicsController \u003c BaseApiController\n  attr_accessor :pagination_options, :sort_options, :filter_options, :include_options\n\n  before_action do |c|\n    # Set a limit on the maximum number of records a user can request per page.\n    # Set the default number of records per page.\n    c.pagination_options = { default_per_page: 10, max_per_page: 60 }\n\n    # Whitelist sortable attributes and set default sort.\n    c.sort_options = {\n       permitted: [:character, :location, :published],\n       default: { id: :desc }\n    }\n\n    # Whitelist filters and configure how to perform the query. There are built-in\n    # query builder classes and custom classes can be plugged in.\n    c.filter_options = [\n       { id: { type: 'Integer' } },\n       { published: { type: 'Date' } },\n       :location,\n       { book: { wildcard: :both } }\n    ]\n\n    # Whitelist inclusions and optionally configure eagerloading.\n    c.include_options = [\n                          {'publisher': -\u003e { includes(:publisher) }},\n                          {'comments': -\u003e { includes(:comments) }},\n                          'comment.author'\n                        ]\n  end\n\n  def index\n    # collect the data\n    builder = JsonApiServer::Builder.new(request, Topic.current)\n     .add_pagination(pagination_options)\n     .add_filter(filter_options)\n     .add_include(include_options)\n     .add_sort(sort_options)\n     .add_fields\n\n   # pass the data to the serializer\n   serializer = TopicsSerializer.from_builder(builder)\n   render json: serializer.to_json, status: :ok\n  end\n\n  def show\n    builder = JsonApiServer::Builder.new(request, Topic.find(params[:id]))\n     .add_include(['publisher', 'comments', 'comments.includes'])\n     .add_fields\n\n    serializer = TopicSerializer.from_builder(builder)\n    render json: serializer.to_json, status: :ok\n  end\n\n  def create\n    topic = Topic.new(topic_params)\n\n    if topic.save\n       serializer = TopicSerializer.new(topic)\n       render json: serializer.to_json, status: :created\n    else\n       render_422(topic)  # return validation errors in JSON API format.\n    end    \n  end\n\n  protected\n\n  def topic_params\n    params.require(:data)\n          .require(:attributes)\n          .permit(:character, :book, :quote, :location, :published,\n                  :author, :publisher_id)\n  end\n\nend\n```\n\n##### 5) Create Serializers\n\nJsonApiServer serializers use the oj gem (https://github.com/ohler55/oj), a fast JSON parser\nand Object marshaller. Helper classes\nJsonApiServer::AttributesBuilder (sparse fieldsets), JsonApiServer::RelationshipsBuilder\n(relationships/included), JsonApiServer::Paginator (pagination), and JsonApiServer::MetaBuilder (meta)\n assist with populating serializers.\n\nCaching is not built into the serializers\ngiven the variability of JSON API documents. Low level caching is recommended.\n\n---\n\nAll serializers inherit from JsonApiServer::BaseSerializer. It creates this structure:\n\n```ruby\n{\n \":jsonapi\": {\n   \":version\": \"1.0\"\n },\n \":links\": null,\n \":data\": null,\n \":included\": null,\n \":meta\": null\n}\n```\n\nSometimes only part of document is needed, i.e., when embedding one serializer in another.\n`as_json` takes an optional hash argument which determines which parts of the document to return.\nThese options can also be set in JsonApiServer::BaseSerializer#as_json_options.\n\n```ruby\nserializer.as_json(include: [:data]) # =\u003e { data: {...} }\nserializer.as_json(include: [:links]) # =\u003e { links: {...} }\nserializer.as_json(include: [:links, :data]) # =\u003e\n# {\n#   links: {...},\n#   data: {...}\n# }\n```\n\n---\n\nJsonApiServer::ResourceSerializer inherits from JsonApiServer::BaseSerializer.\nIt is intended for a single resource (i.e, comment, author). Instantiate with a\nJsonApiServer::Builder instance:\n\n**Example**:\n\n```ruby\nclass TopicSerializer \u003c JsonApiServer::ResourceSerializer\n  resource_type 'topics'\n\n  def links\n    { self: File.join(base_url, \"/topics/#{@object.id}\") }\n  end\n\n  def data\n    {\n      type: self.class.type,\n      id: @object.id,\n      attributes: attributes,\n      relationships: inclusions.relationships\n    }\n  end\n\n  def included\n    inclusions.included\n  end\n\n  protected\n\n  def attributes\n    attributes_builder\n      .add_multi(@object, 'book', 'author', 'quote', 'character')\n      .add('location', @object.location)\n      .add('published', @object.published)\n      .add('created_at', @object.created_at.try(:iso8601, 9))\n      .add('updated_at', @object.updated_at.try(:iso8601, 9))\n      .attributes\n  end\n\n  # Example: provide different ways of accessing the same relationship data:\n  #\n  # 1) 'comments' puts all data in the relationship (easier to walk the tree).\n  # 2) 'comments.includes' puts data in the included section and relationship\n  # links to it.\n  def inclusions\n    @inclusions ||= begin\n      if relationship?('publisher')\n        relationships_builder.relate('publisher', publisher_serializer(@object.publisher))\n      end\n      if relationship?('comments')\n        relationships_builder.relate_each('comments', @object.comments) { |c| comment_serializer(c) }\n      elsif relationship?('comments.includes')\n        relationships_builder.include_each('comments.includes', @object.comments, type: 'comments',\n            relate: {include: [:relationship_data]}) { |c| comment_serializer(c) }\n      end\n\n      relationships_builder\n    end\n  end\n\n  # Pass includes and fields to child serializers.\n  def publisher_serializer(publisher, as_json_options=nil)\n    PublisherSerializer.new(publisher, includes: includes, fields: fields,\n      as_json_options: as_json_options || {include: [:data]})\n  end\n\n  # Pass includes and fields to child serializers.\n  def comment_serializer(comment, as_json_options=nil)\n    CommentSerializer.new(comment,  includes: includes, fields: fields,\n      as_json_options: as_json_options || {include: [:data]})\n  end\nend\n\n# instantiate in controller...\nbuilder = JsonApiServer::Builder.new(request, Topic.find(params[:id]))\n .add_include(['publisher', 'comments', 'comments.includes'])\n .add_fields\nserializer = TopicSerializer.from_builder(builder)\n```\n\nHelper methods:\n\n- JsonApiServer::ResourceSerializer#attributes_builder -- returns an instance of\nJsonApiServer::AttributesBuilder for the 'type' specified in `resource_type`.\n- JsonApiServer::ResourceSerializer#meta_builder -- returns an instance of\nJsonApiServer::MetaBuilder for building the meta section.\n- JsonApiServer::ResourceSerializer#relationships_builder -- returns an instance of\nJsonApiServer::RelationshipsBuilder with whitelisted includes.\n- JsonApiServer::ResourceSerializer#relationship? -- returns true if relationship is\nrequested.\n- JsonApiServer::ResourceSerializer#inclusions? -- returns true if inclusions are\nrequested.\n---\n\nJsonApiServer::ResourcesSerializer inherits from JsonApiServer::ResourceSerializer.\nIt serializes a collection of objects with a specified serializer and merges them.\nIt populates the links section with pagination URLs if JsonApiServer::Builder#add_pagination\nis called.\n\n**Example**:\n\n```ruby\nclass TopicSerializer \u003c JsonApiServer::ResourceSerializer\n  ...\nend\n\nclass TopicsSerializer \u003c JsonApiServer::ResourcesSerializer\n  serializer TopicSerializer # serializer for objects\nend\n\nbuilder = JsonApiServer::Builder.new(request, Topic.current)\n .add_pagination(pagination_options)\n .add_filter(filter_options)\n .add_include(include_options)\n .add_sort(sort_options)\n .add_fields\n\nserializer = TopicsSerializer.from_builder(builder)\n```\n\n## Development\n\n1. Build the docker image:\n\n```shell\ndocker-compose build\n```\n\n2. Start docker image with an interactive bash shell:\n\n```shell\ndocker-compose run --rm gem\n```\n\n3. Once in bash session, code, run tests, start console, etc.\n\n```shell\n# run console with gem loaded\nbundle console\n\n# run tests - to be run from root of gem\nbundle exec rspec\n\n# generate rdoc\nrdoc --main 'README.md' --exclude 'spec' --exclude 'bin' --exclude 'Gemfile' --exclude 'Dockerfile' --exclude 'Rakefile'\n```\n\n## Todo\n\n- Better handling of configs (strings vs symbols). Get rid of HashWithIndifferentAccess if possible (performance penalty).\n- Use MultiJson.\n- Inheritance around serializers is messy.\n- Improve cast class (so its configurable), verify it works with Rails time zones.\n- Clean up tests, esp. as_json which are brittle.\n- Test against Postgres.\n- Support Mongoid.\n- Test against multiple rubies/rails with Travis.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/ed-mare/json_api_server. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fed-mare%2Fjson_api_server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fed-mare%2Fjson_api_server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fed-mare%2Fjson_api_server/lists"}