Routes & Controllers
Igo.js uses Express 5 for routing.
Defining Routes
Routes are defined in /app/routes.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:
// 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
module.exports.api = async (req, res) => {
const users = await User.list();
res.json({ users });
};Redirects
module.exports.login = async (req, res) => {
req.flash('message', 'Welcome back!');
res.redirect('/dashboard');
};Middleware Chain
Igo.js configures the following middleware in order:
- Compression — Gzip (threshold: 1KB)
- Static files —
/publicdirectory (1-year cache in production) - Cookie parser — Populates
req.cookies; verifies cookies written with{ signed: true }intoreq.signedCookies - Session — Encrypted session cookie (31-day expiry)
- Body parsers — URL-encoded and JSON (10MB limit)
- Multipart — File upload parsing via multiparty
- Flash — Flash messages (see Flash)
- Validator — Request validation (see Forms)
- i18n — Language detection (see i18n)
- Locals — Sets
res.locals.env,res.locals.lang - Assets — Webpack manifest injection
- Routes — Your application routes
- Error handler — Catches errors (see Errors)
Static Files
All files in /public are served statically:
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:
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.
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 hoursmaxAge 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.