Skip to content

Instantly share code, notes, and snippets.

@mikermcneil
Last active December 7, 2021 08:44
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikermcneil/5898598 to your computer and use it in GitHub Desktop.
Save mikermcneil/5898598 to your computer and use it in GitHub Desktop.

A Modest Proposal: Events

Sails.js v0.10

Events are a new feature of the Sails core as of 0.9.

Core events

Lifecycle

ready

Called when the Sails app is ready to start accepting requests.

router:before

Called before any of the app's configured static routes have been bound. i.e. a hook might listen to this event to bind some middleware.

router:after

Called after all of the app's configured static routes have been bound. i.e. a hook might listen to this event to bind a "shadow route" to a blueprint.

router:done

Called when all routes have been bound, including those originating from hooks.

router:reset

Called when the router is flushed (i.e. all routes are unbound). The http hook (i.e. Express), and any other attached servers which maintain their own routes should listen for this event so they know to unbind their private routes.

Runtime

router:request

Called when a request is received by the Sails router.

Should receive three arguments, req, res, and next. You know what that means.

router:request:500

Absolute last-resort handler for server errors. Called when a request encounters an error and isn't handled by other means. Should receive three arguments, err, req, and res.

router:request:404

Absolute last-resort handler for requests which don't match any routes. Called when a request doesn't match any routes (or shadow routes, including 404/slug handlers), and this case isn't handled by other means. Should receive two arguments, req, and res.

router:bind

Called when a route is bound.

Should receive a single argument, "routeObj", which looks like:

{
  path: 'String',
  target: function theFnBoundtoTheRoute (req, res, next) {},
  verb: 'String'
}

Hooks

Controllers

  • hook:controllers:loaded
  • hook:controllers:error
  • hook:controllers:bound:crud
  • hook:controllers:bound:actions
  • hook:controllers:bound

Views

  • hook:views:loaded
  • hook:views:error
  • hook:views:bound

CSRF

  • hook:csrf:loaded
  • hook:csrf:error

ORM

  • hook:orm:loaded
  • hook:orm:error

Sockets

  • hook:sockets:loaded
  • hook:sockets:error

Partials

  • hook:partials:loaded
  • hook:partials:error

Request

  • hook:request:loaded
  • hook:request:error

Pubsub

  • hook:pubsub:loaded
  • hook:pubsub:error

Background

  • Developers need an easier way to modify the Sails core for their needs. Hooks and events make this possible! Events are really a necessary feature for hooks to talk to each other and load in the right order.
  • Event-driven design makes for a more modular and extensible code base in general.

How It Works

The instantiated sails object is a Node EventEmitter. That's pretty much it!

Best Practices

Although it can be tempting, it's really best not to add new events to sails in your app code. In general, consistent conventions, clarity, and simplicity are the best practice for developing apps, because it makes them easier to extend, and makes it easier for you to remember how everything works when you come back to it later (not to mention everyone else on your team!)

If you want to add/trigger events to monkeypatch your Sails core, it's best to do this by authoring a hook. More information will show up as we learn more about best practices around that process, but one thing we've definitely learned is that you're better off namespacing your events and firing them on a single object (sails), then emitting and listening on different objects. Why? Sometimes objects get deleted or copied, and this can make a big mess.

If you need a special event in your hook, you will want to namespace it. For instance, if I'm adding a hook called enforceRestfulSesssions that limits the actions that can be added to controllers to encourage code consistency, I might have a hook:enforceRestfulSesssions:checked event that fires when all of the controllers have been checked. This is so that other hooks that know about enforceRestfulSesssions can wait until it has finished its check before proceeding (whether it's just me, or other people on my team, or if I release my hook and it gets popular, other people in the Sails community).

In my hook's initialize method, I might have the following:


// Wait until all the middleware from this app's controllers have loaded
sails.on('hook:controllers:loaded', function () {
  
  // Do stuff
  // e.g. prevent any methods called `login`, `logout` or `signup`
  // since we've opted organizationally for using CRUD on a SessionController instead
  // .....code here........
  
  // When you're done, fire an event in
  sails.emit('hook:enforceRestfulSesssions:checked');
  
});
@CodeOtter
Copy link

hook:orm:prepare would be nice, allowing us to add raw models before the ORM Hook builds them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment