Skip to content

Working with controllers

Marwane Kalam-Alami edited this page Mar 13, 2019 · 9 revisions

Alakajam uses the Express web framework.

Routes

The entry point of all routes is the controllers/index.js file. For instance, the following code registers the maincontroller.events(req, res) method to be called everytime a user reaches the /events URL:

router.all('/events', mainController.events)

Controllers

When entering the events(req, res) method, we will have access to everything we need about:

  • The request (req), in particular the query parameters (req.query) and form fields (req.body) ;
  • The response (res), mostly to access context info (eg. res.locals.user, res.locals.event) and trigger the rendering of a page res.render(templateName, context).

Typically, a controller has the following structure:

myController(req, res) {
  // Read the request parameters
  // Make relevant security checks
  // Sanitize form data if needed (see core/forms.js)
  // Call one or more services to gather/process data
  // Render a page
}

Note that controllers are not meant to hold complicated algorithms nor database requests, instead delegating them to the services (see services/ folder).

Filter middlewares

Before the web requests even reach your controller, the res.locals object is pre-filled with relevant variables according to the context. For instance, if the request comes from a logged user, res.locals.user is always set for you.

This is made possible by registering request "filters" or "middleware" used to avoid code repetition:

router.use('*', mainController.anyPageMiddleware)

This special route means that mainController.anyPageMiddleware(req, res, next) will be called for every single page request to the server. And that's where we set up data like res.locals.user.

ℹ️ You may notice that controllers and filters are both registered the same way. Technically, for Express, they are both middleware. The only difference is how anyPageMiddleware has next in its signature, meaning you can call next() from it to forward the request to any other middleware. Controllers on the other hand never call next(), making them the final handlers. Make sure in your controllers that all scenarios end with a res.render or res.errorPage, otherwise the request will hang forever.

Error handling

If an error is thrown from your code, it will be caught appropriately and rendered as a 500 error page. You can also trigger error pages manually (eg. for "404 Not found" or "401 Forbidden" errors) by calling res.errorPage(code, error)(*) instead of res.render().

If you're curious, this is made possible by custom methods and a special error handling middleware set up in core/middleware.js.

(*) res.errorPage is not standard Express code but a homemade extension.