1 Add basic REST endpoints for a collection
Scott Erickson edited this page 2016-04-22 16:01:23 -07:00

Problem

You want the server to provide standard REST endpoints to the site.

Solution

Create routes in /server/routes/index.coffee, using pre-existing or custom middleware in /server/middleware.

Details

The routes we usually include for a collection are:

  • Create: POST /db/collection_name
  • Get many: GET /db/collection_name
  • Get one: GET /db/collection_name/:id
  • Edit one: PUT /db/collection_name/:id

Some collections may also allow DELETE, though that's uncommon. Others may not need all of these, such as one which allows creation but not editing.

General vs Custom Behavior

There are generic handlers for standard behavior in /server/middleware/rest.coffee, and if possible, your endpoints should just use those. If your endpoint needs custom behavior, though, you can use rest.coffee as a template for specific middleware, such as for the PUT Achievement middleware.

You can also extend route behavior by chaining middleware, such as these Achievement POST and GET routes do. The GET middleware fetchByRelated checks if there's a related is a GET query parameter. If there is, it handles the response, otherwise it continues to the generic rest GET middleware.

app.get('/db/achievement', mw.achievements.fetchByRelated, mw.rest.get(Achievement))

While the POST middleware prevents anyone but admins or artisans from performing the action.

app.post('/db/achievement', mw.auth.checkHasPermission(['admin', 'artisan']), mw.rest.post(Achievement))

Generators and Yield

To avoid callback hell, we use co, which is elegantly tied to express with co-express. Async functions should return Promises, and then yielded to co. Mongoose commands like find and save return Promise-like Query objects. If you need to make a Node-style callback function into a Promise, use Bluebird. When errors happen, they are automatically given to Express' next function and sent to our error middleware.

For an elegant explanation of what's going on when you use co and yield, read Node.js Flow (part 2) - Fibers and Generators by Eirik Vullum.

wrap = require 'co-express'

# this function is express middleware that can be put into any routes
module.exports.setSomeValue: wrap (req, res) ->
  doodad = yield Doodad.findById(req.query.userID)
  doodad.set('property', 'value')
  yield doodad.save()
  res.status(200).send(doodad.toObject())

Resources