Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?


Failed to load latest commit information.
Latest commit message
Commit time


Gem Version Code Climate Test Coverage Build Status

A JSON API client for Ruby


Add this line to your application's Gemfile:

gem 'munson'

And then execute:

$ bundle

Or install it yourself as:

$ gem install munson

Basic Usage

class Article < Munson::Resource
  # This is done automatically if the tableize method is present
  self.type = :articles

  # how to process the JSON API ID. JSON API always uses strings, but Munson defaults to :integer
  key_type :integer, #:string, ->(id){ }
  attribute :title, :string

Registering the JSONAPI 'type'

Calling Article.type = :articles registers the class Article as the handler for JSON API resources of the type articles.

When the ActiveSupport method tableize is present, this will be set automatically. A class can be bound to multiple types:

Article.type = :articles
Munson.register_type(:posts, Article)


articles = Article.fetch # Get some articles
article = Article.find(9) # Find an article

query = Article.fields(:title).include(:author, :comments).sort(id: :desc).filter(published: true)
query.to_params #=> {:filter=>{:published=>"true"}, :fields=>{:articles=>"title"}, :include=>"author,comments", :sort=>"-id"}
query.to_query_string #=> "fields[articles]=title&filter[published]=true&include=author,comments&sort=-id"

query.fetch # Fetch articles w/ the given query string

The Munson::Resource delegates a few methods to its underlying Munson::Connection:


query = Product.filter(min_price: 30, max_price: 65)

# its chainable
query.filter(category: 'Hats').filter(size: ['small', 'medium'])

#=> {:filter=>{:min_price=>"30", :max_price=>"65", :category=>"Hats", :size=>"small,medium"}}

query.fetch #=> Munson::Collection<Product,Product>


query = Product.sort(created_at: :desc)

# its chainable
query.sort(:price) # defaults to ASC

#=> {:sort=>"-created_at,price"}

query.fetch #=> Munson::Collection<Product,Product>

Including (Side loading related resources)

query = Product.include(:manufacturer)

# its chainable

#=> {:include=>"manufacturer,vendor"}

query.fetch #=> Munson::Collection<Product,Product>

Sparse Fieldsets

query = Product.fields(products: [:name, :price])

# its chainable
query.include(:manufacturer).fields(manufacturer: [:name])

#=> {:fields=>{:products=>"name,price", :manufacturer=>"name"}, :include=>"manufacturer"}

query.fetch #=> Munson::Collection<Product,Product>

All the things!

query = Product.
  filter(min_price: 30, max_price: 65).
  sort(popularity: :desc, price: :asc).
  fields(product: ['name', 'price'], manufacturer: ['name', 'website']).
  page(number: 1, limit: 100)


#=> {:filter=>{:min_price=>"30", :max_price=>"65"}, :fields=>{:product=>"name,price", :manufacturer=>"name,website"}, :include=>"manufacturer", :sort=>"-popularity,price", :page=>{:limit=>10}}

query.fetch #=> Munson::Collection<Product,Product>

Fetching a single resource

Product.find(1) #=> product

Accessing Munson internals

Every Munson::Resource has an internally managed client. It is accessible via .munson

Article.munson #=> Munson::Client
Article.munson.path #=> base path to use for this resource. Defaults to "/" + type; i.e., "/articles"
Article.munson.agent #=> Munson::Agent: the agent wraps the Munson::Connection. It performs the low level GET/POST/PUT/PATCH/DELETE methods
Article.munson.query #=> Munson::Query: a chainable query building instance
Article.munson.connection #=> Munson::Connection: Small wrapper around the Farady connection
Article.munson.connection.response_key_format #=> :dasherize, :camelize, nil
Article.munson.connection.url #=> This endpoints base URL
Article.munson.connection.faraday #=> The faraday object for this connection
Article.munson.connection = SomeNewConnectionYouPrefer
Article.munson.connection.configure(opts) do { |faraday_conn| } #=> Feel free to reconfigure me ;D

Persistence Resources

class Article < Munson::Resource
  attribute :title, :string
  attribute :body, :string
  attribute :created_at, :time

Creating a new resource

article =
article.title = "This is a great read!" #=> Boolean: Will attempt to POST to /articles
article.errors #=> Array of errors

Updating a resource

article = Article.find(9)
article.title = "This is a great read!" #=> Boolean: Will attempt to PATCH to /articles
article.errors #=> Array of errors

Accessing Side Loaded Resources

Given the following relationship:

class Article < Munson::Resource
  self.type = :articles
  has_one :author
  has_many :comments

  key_type :integer
  attribute :title, :string

class Person < Munson::Resource
  self.type = :people
  has_many :articles

  attribute :first_name, String
  attribute :last_name, :string
  attribute :twitter, :string
  attribute :created_at, :time, default: ->{ }, serialize: ->(val){ val.to_s }
  attribute :post_count, :integer

class Comment < Munson::Resource
  self.type = :comments
  has_one :author

  attribute :body, ->(val){ val.to_s }
  attribute :score, :float
  attribute :created_at, :time
  attribute :is_spam, :boolean
  attribute :mentions, :string, array: true

Note: When specifying relationships in Munson, you are specifying the JSON API type name. You'll notice below that when .author is called it returns a person object. That it is because in the HTTP response, the relationship name is author but the resource type is people.

  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON API paints my bikeshed!"
    "relationships": {
      "author": {
        "links": {
          "self": "",
          "related": ""
        "data": { "type": "people", "id": "9" }

Munson initializes objects for side loaded resources. Only 1 HTTP call is made.

article = Article.include(:author, :comments).find(9) #=> Person object
article.comments #=> Munson::Collection<Comment> #=> Chauncy


Munson is designed to support multiple connections or API endpoints. A connection is a wrapper around Faraday::Connection that includes a few pieces of middleware for parsing and encoding requests and responses to JSON API Spec.

Setting the default connection:

Munson.configure(url: '') do |c|
  c.use MyCustomMiddleware
  c.use AllTheMiddlewares

Each Munson::Resource has its own Munson::Client. The client copies the default connection so its easy to set general configuration options, and overwrite them on a resource by resource basis.

Munson.configure(url: '', response_key_format: :dasherize)

class Kitten < Munson::Resource
  munson.url = ""

# Overwritten URL
Kitten.munson.connection.url #=> ""
# Copied key format
Kitten.munson.connection.response_key_format #=> :dasherize

Munson.default_connection.url #=> ""
Munson.default_connection.response_key_format #=> :dasherize

Configuration Options

Munson.configure(url: '', response_key_format: :dasherize) do |conn|
  conn.use SomeCoolFaradayMiddleware

Two special options can be passed into .configure:

  • url the base url for this endpoint
  • response_key_format the format of the JSONAPI response keys. Valid values are: :dasherize, :camelize, nil

Additinally any Faraday Connection options can be passed. [Faraday::Connection options]( Faraday::Connection)

Advanced Usage

Custom Query Builder

Since the filter param's format isn't specified in the spec this implementation uses JSONAPI::Resource's implementation

To override, implement your own custom query builder inheriting from Munson::Query. Munson::Client takes a Query class to use. This method could be overwritten in your Resource:

class MyBuilder < Munson::Query
  def filter_to_query_value
    # ... your fancier logic

Article.munson.query_builder = MyBuilder
Article.munson.filter(:name => "Chauncy") #=> MyBuilder instance

Without inheriting from Munson::Resource

If for some reason you cannot inherit from Munson::Resource, you can still get a lot of JSONAPI parsing functionality

class Album
  # Just some attr accessors, NBD
  attr_accessor :id
  attr_accessor :title

  # Give Album a client to use
  def self.munson
    return @munson if @munson
    @munson =

  # Set the type, note, this is not being set on self
  munson.type = :albums

  # Register the type w/ munson
  Munson.register_type(munson.type, self)

  # When you aren't inherited from Munson::Resource, Munson will pass a Munson::Document to a static method called munson_initializer for you to initialze your record as you wish
  def self.munson_initializer(document)
    new(, document.attributes[:title])

  def initialize(id, title)
    @id    = id
    @title = title
albums = Album.munson.include(:songs).fetch
albums.first.title #=> An album title!

Any ol' object (Register type, add munson_initializer)

As long as a class is registered with munson and it response to munson_initializer, Munson will be able to initialize the object with or without a client

Extending the example above...

class Song
  attr_reader :name
  def self.munson_initializer(document)

  def initialize(name)
    @name = name

Munson.register_type :songs, Song
album = Album.munson.include(:songs).find(9)
album.songs #=> Munson::Collection<Song>


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


Bug reports and pull requests are welcome on GitHub at Base hotfixes off of the master branch and features off of the develop branch.


  • Update Yard docs :D
  • Posting/Putting relationships
  • A few pending tests :/
  • Collection#next (queries for next page, if pagination present)
  • Related Documents/Resources taking advantage of underlying resource[links]
  • Error object to wrap an individual error
  • consider enumerable protocol on a query
  • Handle null/empty responses...
  • Pluggable pagination?
  • Query#find([...]) find multiple records


JSON API Spec client for Ruby







No packages published

Contributors 4