Skip to content

Routes & Controllers

Igo.js uses Express 5 for routing.

Defining Routes

Routes are defined in /app/routes.js:

js
module.exports.init = function(app) {
  app.get('/',            controllers.WelcomeController.index);
  app.get('/users',       controllers.UserController.list);
  app.get('/users/:id',   controllers.UserController.show);
  app.post('/users',      controllers.UserController.create);
  app.put('/users/:id',   controllers.UserController.update);
  app.delete('/users/:id', controllers.UserController.remove);
};

See the Express routing documentation for the full routing API.

Controllers

Controllers are plain modules in /app/controllers/. Each export is a request handler:

js
// app/controllers/UserController.js

module.exports.list = async (req, res) => {
  const users = await User.list();
  res.render('users/list', { users });
};

module.exports.show = async (req, res) => {
  const user = await User.find(req.params.id);
  if (!user) return res.status(404).render('404');
  res.render('users/show', { user });
};

module.exports.create = async (req, res) => {
  const form = new UserForm().submit(req);
  if (form.errors) {
    req.flash('form', form);
    return res.redirect('/users/new');
  }
  await User.create(form.getValues());
  res.redirect('/users');
};

JSON Responses

js
module.exports.api = async (req, res) => {
  const users = await User.list();
  res.json({ users });
};

Redirects

js
module.exports.login = async (req, res) => {
  req.flash('message', 'Welcome back!');
  res.redirect('/dashboard');
};

Middleware Chain

Igo.js configures the following middleware in order:

  1. Compression — Gzip (threshold: 1KB)
  2. Static files/public directory (1-year cache in production)
  3. Cookie parser — Populates req.cookies; verifies cookies written with { signed: true } into req.signedCookies
  4. Session — Encrypted session cookie (31-day expiry)
  5. Body parsers — URL-encoded and JSON (10MB limit)
  6. Multipart — File upload parsing via multiparty
  7. Flash — Flash messages (see Flash)
  8. Validator — Request validation (see Forms)
  9. i18n — Language detection (see i18n)
  10. Locals — Sets res.locals.env, res.locals.lang
  11. Assets — Webpack manifest injection
  12. Routes — Your application routes
  13. Error handler — Catches errors (see Errors)

Static Files

All files in /public are served statically:

js
app.use(express.static('public'));

In production, static files are served with a 1-year max-age cache header.

File Uploads

Multipart POST requests are automatically parsed. Fields go to req.body, files to req.files:

js
module.exports.upload = async (req, res) => {
  if (req.files && req.files.avatar) {
    const file = req.files.avatar;
    // file.name — original filename
    // file.path — temporary path
    // file.size — file size in bytes
  }
};

Cookies & Sessions

cookie-parser populates req.cookies and verifies cookies written with { signed: true } into req.signedCookies. The session lives in a single cookie encrypted with AES-256-GCM: unreadable and untamperable client-side.

js
config.cookieSecret         = 'your-secret-key';    // signs req.signedCookies
config.cookieSession.keys   = ['your-session-key']; // encrypts the session
config.cookieSession.maxAge = 24 * 60 * 60 * 1000;  // 24 hours

maxAge is embedded in the encrypted payload and checked server-side: a copied cookie stops working past its expiry, even if the client keeps sending it.

keys accepts several entries: the first encrypts (HKDF-SHA256 derives the AES key from it), the others decrypt only. Handy to change the key without signing everyone out — but a leaked key must be dropped, not kept around, since anyone holding it can forge a session.

Upgrading from igo < 6.2

Sessions used to be base64-encoded and signed, ie. readable client-side. Those cookies are rejected: every session is dropped on deploy and users have to sign in again.

Released under the ISC license.