Frameworks can feel like a pile of conventions to memorize. They become easier to learn once you notice that nearly all of them do the same five things, in the same order, for every request. Learn that map once and you can find your way around Noderyx, Express, Laravel, Rails, or Django.
The five stops of a request
1. The router matches the URL
The router is a lookup table from method and path to a handler. Patterns like /category/:slug capture parts of the URL as parameters. When a page shows the wrong content, start with the router: is the URL actually reaching the handler you think it is?
2. Middleware guards the door
Middleware runs before the handler and can stop the request early. Typical jobs include authentication, rate limiting, CSRF checks, and redirects. This project protects the entire /nodecms namespace with one function:
export function requireCmsAuthentication(context, next) { const { pathname } = new URL(context.request.url, "http://localhost"); const isCms = pathname === "/nodecms" || pathname.startsWith("/nodecms/"); if (!isCms || pathname === "/nodecms/login" || context.session?.user) return next(); context.response.baseHeaders.location = "/nodecms/login"; return context.text("", 303); }
Guarding a namespace, rather than individual routes, means a newly added admin page is protected by default. Secure defaults like this are one of the best reasons to put logic in middleware.
3. The controller coordinates
A controller action reads the request, calls services, and chooses a response. It should read like a short story: find the post, return 404 if it is missing, otherwise render it. When a controller grows long, logic is usually leaking into it from a service or a view.
4. Services own the data and the rules
Services hold the knowledge that does not depend on HTTP. In this project, CmsStore decides what "published" means, sorts the feed, and records revisions. Because it knows nothing about requests, the same service powers the website, the admin, and command-line scripts such as the seeder.
Services are also where storage choices hide. CmsStore works with a file driver or a MySQL driver, chosen by the CMS_STORAGE setting. No controller had to change to support both.
5. The view presents
Views turn data into HTML and should contain as little logic as possible: conditionals for optional parts and loops for lists. Escaping output by default, as Noderframe does with {{ }}, stops content from becoming code.
A debugging habit that uses the map
- Wrong page or 404? Check the router first.
- Redirected unexpectedly? Check middleware.
- Right page, wrong data? Check the controller's service calls.
- Right data, wrong output? Check the view and the stylesheet.
Give each layer one responsibility. When you need to change something, the map tells you which file to open, and code review gets easier because each change has an obvious home.
Comments