Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

pavarotti

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pavarotti

pavarotti creates CRUD controllers given a mongoose model

  • 0.0.1
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
1
Maintainers
1
Weekly downloads
 
Created
Source

pavarotti - easily create CRUD controllers

We consistently require controllers that have CRUD functionality (create, read, update and delete).

To avoid institutionalising ourselves, we define generic versions of these methods here which we can then use to e.g. build a controller that manages users. Such a controller would be created with usersController = require('pavarotti').methodsFor(User), where User is the user model.

Methods

The methods we would like to build abstract versions of, are:

  • set

    Create an item with the given params, or update the item with given id with the given params. The newly saved item is returned.

  • get

    Get a single item by its id.

  • find

    Get all items that match the criteria given, possibly paging and sorting the result.

  • remove

    Delete a single item by its id.

Config

We can customise these methods by passing in configuration options to the main methodsFor method, which constructs the CRUD controller. The options are:

set
  • beforeSet : Model |-> Model

    bfunction called before the model is saved to the database. Is given the model that will be saved. The result is the final object saved to the database. An error stops the method.

    Defaults to bf.identity.

  • afterSet : Model |-> Model

    bfunction called after the model has been saved to the database. Is given the model that was saved. The result will be the output of the set method. An error stops the method.

    Defaults to bf.identity.

get
  • afterGet: : Model |-> Model

    bfunction called after the model has been retrieved from the database. Is given the model that was retrieved. The result will be the output of the get method. An error stops the method.

    Defaults to bf.identity.

find
  • beforeFind: : Params |-> Params

    bfunction called at the beginning of the find method. Is given the params to the find method. The result will be fed into the next stem of the find method. An error stops the method.

    Defaults to bf.identity.

  • buildFindFilter : Params |-> FindFilter

    bfunction that, given the params to the find method, builds the object that is given to mongoose to filter the model collection (the find() filter). If null, builds the find filter using properties that are in the filter object in the params.

    Defaults to null.

  • buildFindSort : Params |-> FindSort

    bfunction that, given the params to the find method, builds the object that is given to mongoose to sort the model collection (the sort() object). If null, builds the sort filter using properties thare are in the sort object in the params such that e.g. { sort: { name: 'asc' } } becomes { name: 1 }.

    Defaults to null.

  • afterFind: `: { items: Model[], total: number, filteredTotal: number } |-> any

    bfunction that, given the final result of the find method (results, number of total items and number of filtered items) returns some object that will be the output of the find method.

    Defaults to bf.identity.

Dependencies

_           = require 'underscore'
bf          = require 'barefoot'

Abstraction

The CRUD controller methods are generated once from a single initialisation call to methodsFor. This method takes the model that the CRUD methods are for and a variety of additional configuration methods that lets the consumer of this CRUD controller customise each of the CRUD methods.

methodsFor = (model, config = {}) ->

  config = _.defaults config,
    beforeSet: bf.identity
    afterSet: bf.identity

    afterGet: bf.identity

    beforeFind: bf.identity
    buildFindFilter: null
    buildFindSort: null
    afterFind: bf.identity

  crud = {}

Create and update (set)

We create and update with the single set method. This performs an insert operation if no id is given, and an update operation is an id is given. In any case, the newly saved item is returned.

  crud.set =

    bf.chain -> [
      bf.validate
        _id: String

      getOrCreate
      config.beforeSet
      save
      config.afterSet
    ]

First, attempt to retrieve the item by its id. If there is none found then create a new item. We use mongoose's set method to apply the given params to the fetched model.

  getOrCreate = (params, done) ->

    w = bf.errorWrapper done

    model.findById params.id, w (item) ->

      item ?= new model()
      item.set params

      done null, item

Now, we have a fully populated offer model. All that's left to do is save it!

  save = (item, done) ->
    item.save done

Read (get)

We can retrieve a single item by its id very simply. This method takes one param, id, and returns the item (or null if none exists).

  crud.get =

    bf.chain -> [
      bf.validate
        id: String

      bf.select (p) -> p.id
      _.bind model.findById, model
      config.afterGet
    ]

Read (find)

We can search for items using a single super method that takes many different filtering params (findFilterParams) and sorting params (findSortParams) and retrieves a paginated list of items that match. The consumer of the CRUD API helper is able to modify the mongo find() object and sort() object.

  crud.find =

    bf.chain -> [
      bf.validate
        _filter: Object
        _sort: Object
        _skip: Number
        _limit: Number

      config.beforeFind
      bf.parallel [
        config.buildFindFilter ? buildFilter
        config.buildFindSort ? buildSort
        bf.identity
      ]
      runQuery
      config.afterFind
    ]

The default buildFindFilter just filters on the parameters given in params.filter.

  buildFilter = (params, done) ->

    done null, params.filter

The default buildFindSort convers the params.sort object given from e.g. {prop: 'asc'} to {prop: 1}.

  buildSort = (params, done) ->

    sort = null

    if params.sort?
      sort = {}
      for p, o of params.sort
        if o == 'asc'
          sort[p] = 1
        else if o == 'desc'
          sort[p] = -1

    done null, sort

We require very flexible pagination, so running the query is a little tedious, though simple enough.

  runQuery = ([filter, sort, params], done) ->

    seq = bf.sequence done

    filter ?= {}
    sort ?= {}

    total = 0
    filteredTotal = 0

    seq.then (next) ->
      model.find().count seq.w (count) ->
        total = count
        next()

    seq.then (next) ->
      model.find(filter).count seq.w (count) ->
        filteredTotal = count
        next()

    seq.then (next) ->
      query = model.find(filter)

      if params.skip?
        query = query.skip(params.skip)
      if params.limit?
        query = query.limit(params.limit)

      query.exec seq.w (items) ->
        done null,
          items: items
          total: total
          filteredTotal: filteredTotal

Delete (remove)

Deleting or removing an item is a simple method that retrieves an object by its id and then removes it if it exists. If no item with the given id exists, nothing happens.

  crud.remove =

    bf.chain -> [
      bf.validate
        id: String

      _.bind model.remove, model
    ]

Finishing up

We must conclude the methodsFor function we began to define.

  return crud

And then export this function.

module.exports = {
  methodsFor
}

FAQs

Package last updated on 07 Aug 2013

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc