Filtering

View the JSONAPI specification

View the YARD Documentation

View the Sample App: Server1Server2Client

View the JS Documentation

Filters are usually one-liners, with the logic delegated to an Adapter.

allow_filter :title

You can view allow_filter like a whitelist. We wouldn’t want to automatically support filters - otherwise sneaky users might filter our Employees to only those making a certain salary. Hence the whitelist.

To customize a filter:

allow_filter :title do |scope, value|
  scope.where(title: value)
end

A real-life example might be a prefix query:

allow_filter do |scope, value|
  scope.where(["title LIKE ?", "#{value}%"])
end

Filtering Relationships

Prefix the filter parameter with the relevant JSONAPI Type like so:

/blogs?include=posts&filter[posts][title]=foo

Default Filters

View the YARD Documentation

You may want your scope to be filtered any time it is accessed - Perhaps you only want to show active posts by default:

default_filter :active do |scope|
  scope.where(active: true)
end

Default filters can be overridden if there is a corresponding allow_filter. Given a Resource:

allow_filter :active

default_filter :active do |scope|
  scope.where(active: true)
end

And the following requests:

/posts
/posts?filter[active]=false

The first will display only active posts, the second will display only inactive posts.

Filter Conventions

There are some common naming conventions for supporting more complex filters:

# greater than
allow_filter :id_gt

# greater than or equal to
allow_filter :id_gte

# less than
allow_filter :id_lt

# less than or equal to
allow_filter :id_lte

# prefix queries
allow_filter :title_prefix

# OR queries
allow_filter :active_or # true or false

NOTE: AND queries are supported by default - just pass a comma-delimited list of values.

Filter Guards

You can conditionally allow filters based on runtime context. Let’s say only managers should be allowed to filter employees by salary:

allow_filter :salary, if: :manager?

def manager?
  current_user.role == 'manager'
end

Filter Aliases

Aliases mostly come into play when supporting backwards compatibility. Let’s say we originally called the filter fname then later wanted the more-expressive first_name. An alias allows is to keep a one-liner with the correct naming, while still responding correctly to fname:

allow_filter :first_name, aliases: [:fname]

Accessing Runtime Context

allow_filter can access runtime context (in Rails, the controller) as the last argument:

allow_filter :my_siblings do |scope, value, context|
  if value == true
    scope.where(family_id: context.current_user.family_id)
  else
    scope
  end
end