sluicebox

Quickly and elegantly add search, sort and pagination to your plural resource endpoints.

Usage no npm install needed!

<script type="module">
  import sluicebox from 'https://cdn.skypack.dev/sluicebox';
</script>

README

SluiceBox

Add search, sort and pagination to your plural resource endpoints.

Getting Started

Install Sluice Box:

npm install sluicebox --save

Overview

SluiceBox is a bundle of helpers for creating robust and feature-rich plural resource endpoints. It has helpers for pagination, searching and sorting. It is currently focused on resources stored in MongoDB, but support for other persistent stores may be added in the future. Sluicbox pagination works particularly well with the express-lane enhanced routing library. Sluicebox searching, sorting and pagination works particularly well with the meerkat (and/or timon) wrapper for the mongo-native driver.

Searching

Searches are expressed as a collection of clauses joined by conjunctions: ;(and) to further refine a search, |(or) to broaden the search. Clauses may be either simple predicates or groups of subordinate clauses joined together by conjunctions and wrapped in parenthesis (...). Predicates are composed of a field name and a value separated by a comparison. Comparisons are either a lone comparator or a !(not) followed by a comparator. The currently supported comparators are: :(equal), *(contains), ^(starts with), $(ends_with), <(less than) and >(greater than).

A simple search to find all users that have a gmail email address could be expressed as:

/api/versions/1/users?q=email$gmail.com

To refine our search to only active users we can simply add another predicate:

/api/versions/1/users?q=email$gmail.com;state:active

A complex search to find all active users with either gmail or yahoo addresses could be expressed as:

/api/versions/1/users?q=state:active;(email$gmail.com|email$yahoo.com)

The Code

This example contains constructs from express-lane and timon for simplicity.

sluicebox = require('sluicebox')()
{ Account } = require '../models'
module.exports =
  get: (req, res, next) ->
    sluicebox.search req.query.q,
      (query) ->
        Account(req).find(query).all (accounts) ->
          res.json { accounts }
      (error) ->
        res.status(422).json error

Sorting

Sorting is specified as a comma separated list of one or more sort descriptors. A sort descriptor is a field name optionally followed by a direction. A direction is one of asc or desc. When a direction is not specified the descriptor defaults to asc.

A simple sort that orders the results lexicographically by last name could be expressed as:

/api/versions/1/users?sort=name.last asc

or

/api/versions/1/users?sort=name.last

A simple sort that orders the most recently created accounts first could be expressed as:

/api/verisons/1/users?sort=created_at desc

A compound sort that orders the results lexicographically by last name and orders people who have the last name lexicographically by first name could be expressed as:

/api/versions/1/users?sort=name.last,name.first

The Code

Lets add sorting to our previous example.

sluicebox = require('sluicebox')()
{ Account } = require '../models'
module.exports =
  get: (req, res, next) ->
    sluicebox.search req.query.q,
      (query) ->
        sort = sluicebox.sort req.query.sort
        Account(req).find(query).sort(sort).all (accounts) ->
          res.json { accounts }
      (error) ->
        res.status(422).json error

Pagination

The pagination complete with hypermedia can be easily added to result sets. This is particularly easy when paired with meerkat or timon.

The third page of a result set with 20 results per page could be requested as:

/api/versions/1/users?page=3&per=20

The Code

Lets add pagination to our previous example.

sluicebox = require('sluicebox')()
{ Account } = require '../models'
module.exports =
  get: (req, res, next) ->
    sluicebox.search req.query.q,
      (query) ->
        sort = sluicebox.sort req.query.sort
        page = req.query.page ? 1
        per = req.query.per ? 100
        Account(req).find(query).sort(sort).paginate page, per, (accounts, pagination) ->
          pagination = sluicebox.paginate 'accounts', req, res, pagination
          res.json { accounts, pagination }
      (error) ->
        res.status(422).json error

Contributing

In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality.

Release History

(Nothing yet)