MVC Example
This page documents the Model-View-Controller (MVC) example included in the Express repository. It demonstrates how to structure an Express application using the MVC pattern, including automatic route generation from controller files and a faux database model for data storage.
Overview
The MVC example shows how to organize an Express application into separate concerns: models (data), views (templates), and controllers (request handling logic). The example includes automatic route generation, where controller files placed in a controllers directory are automatically scanned and have routes generated based on their exported methods. A faux database module provides sample data for pets and users, demonstrating CRUD operations within the MVC structure.
How Routes Are Structured
Routes in the MVC example are generated automatically from controller files. The route generation logic resides in examples/mvc/lib/boot.js, which reads each controller file from the controllers directory and inspects its exported methods. The boot script maps specific method names to HTTP verbs and URL patterns:
| Exported Method | HTTP Verb | Route Pattern | Purpose |
|---|---|---|---|
list | GET | /{controller_name} | List all resources |
show | GET | /{controller_name}/:id | Show a single resource |
edit | GET | /{controller_name}/:id/edit | Show edit form |
update | PUT | /{controller_name}/:id | Update a resource |
create | POST | /{controller_name} | Create a resource |
index | GET | /{controller_name} | Redirect or default action |
The controller name is derived from the directory name under controllers. For example, a controller at controllers/pet/index.js generates routes prefixed with /pet. Controllers can also specify a custom prefix property to override the default route prefix, as demonstrated by the user-pet controller which uses prefix: '/user/:user_id' examples/mvc/controllers/user-pet/index.js.
Each controller can export a before middleware function that runs before any route handler in that controller. This middleware is used for tasks like loading a resource by ID from the database and attaching it to the request object.
How Controllers Are Automatically Loaded
The automatic loading process is handled by examples/mvc/lib/boot.js. The main application file, examples/mvc/index.js, sets up Express and then calls the boot script:
// From examples/mvc/index.js (conceptual)
var boot = require('./lib/boot');
boot(app);The boot script performs the following steps:
- Reads the
controllersdirectory to discover all subdirectories - For each subdirectory, requires its
index.jsfile - Inspects the exported methods of each controller
- Generates Express routes based on the method names and their corresponding HTTP verbs
- Applies any
beforemiddleware before the route handlers
The boot script checks for the following method names and maps them to routes: list, show, edit, update, create, and index. If a controller exports a before function, it is used as middleware for all routes in that controller. The before function receives req, res, and next parameters and can call next('route') to skip to the next matching route if the resource is not found examples/mvc/controllers/pet/index.js:12-16.
The Faux Database Model
The faux database is defined in examples/mvc/db.js. It provides simple JavaScript arrays of objects that serve as the data layer for the example:
// From examples/mvc/db.js (conceptual)
exports.pets = [
{ id: 1, name: 'Tobi' },
{ id: 2, name: 'Loki' },
{ id: 3, name: 'Jane' }
];
exports.users = [
{ id: 1, name: 'tj' },
{ id: 2, name: 'tobi' },
{ id: 3, name: 'loki' }
];This module exports two arrays: pets and users. Controllers access this data by requiring the db module and querying the arrays directly. The database is intentionally simple to focus on the MVC pattern rather than actual database integration.
CRUD Operations
The MVC example demonstrates Create, Read, Update, and Delete operations through its controllers:
Read Operations
The user controller exports a list method that renders all users, and a show method that displays a single user. The pet controller exports a show method that displays a single pet. Both controllers use before middleware to load the resource by ID from the database before the route handler executes.
Update Operations
The pet controller exports an update method that handles PUT requests:
// From examples/mvc/controllers/pet/index.js:24-28
exports.update = function(req, res, next){
var body = req.body;
req.pet.name = body.pet.name;
res.message('Information updated!');
res.redirect('/pet/' + req.pet.id);
};This method updates the pet's name from the request body, sets a flash message using res.message(), and redirects to the pet's show page. The user controller similarly exports an update method.
Create Operations
The user-pet controller exports a create method that handles POST requests for creating a new pet under a specific user. This controller uses a custom prefix of /user/:user_id to nest the pet creation under a user resource.
Edit Operations
Both the pet and user controllers export edit methods that render edit forms for their respective resources. These methods use the before middleware to ensure the resource exists before rendering the form.
Controller Documentation
Main Controller (examples/mvc/controllers/main/index.js)
Purpose: Provides the root redirect for the application.
Key File: examples/mvc/controllers/main/index.js
Public API:
| Symbol | Signature | Purpose |
|---|---|---|
index | function(req, res) | Redirects root URL / to /users |
This controller has a single method that redirects visitors from the root path to the users list.
Pet Controller (examples/mvc/controllers/pet/index.js)
Purpose: Manages CRUD operations for pet resources.
Key File: examples/mvc/controllers/pet/index.js
Public API:
| Symbol | Signature | Purpose |
|---|---|---|
engine | 'ejs' | Specifies EJS as the view engine for this controller |
before | function(req, res, next) | Loads pet by ID from database, calls next('route') if not found |
show | function(req, res, next) | Renders the show view with the loaded pet |
edit | function(req, res, next) | Renders the edit view with the loaded pet |
update | function(req, res, next) | Updates pet name from request body, sets message, redirects |
The before middleware loads the pet from db.pets using req.params.pet_id and attaches it to req.pet. If the pet is not found, it calls next('route') to skip to the next matching route.
User Controller (examples/mvc/controllers/user/index.js)
Purpose: Manages CRUD operations for user resources.
Key File: examples/mvc/controllers/user/index.js
Public API:
| Symbol | Signature | Purpose |
|---|---|---|
engine | 'ejs' | Specifies EJS as the view engine for this controller |
before | function(req, res, next) | Loads user by ID from database, calls next('route') if not found |
list | function(req, res, next) | Renders the list view with all users |
show | function(req, res, next) | Renders the show view with the loaded user |
edit | function(req, res, next) | Renders the edit view with the loaded user |
update | function(req, res, next) | Updates user information from request body |
This controller follows the same pattern as the pet controller but for user resources. It exports a list method to display all users, which the main controller redirects to from the root URL.
User-Pet Controller (examples/mvc/controllers/user-pet/index.js)
Purpose: Handles creating pets under a specific user.
Key File: examples/mvc/controllers/user-pet/index.js
Public API:
| Symbol | Signature | Purpose |
|---|---|---|
name | 'pet' | Specifies the resource name for route generation |
prefix | '/user/:user_id' | Custom route prefix for nested resource |
create | function(req, res, next) | Creates a new pet for the specified user |
This controller demonstrates nested resource routing. By setting prefix: '/user/:user_id', the generated routes become /user/:user_id/pet for the create action. The name property overrides the default controller name for route generation.
Comparison of Controllers
| Aspect | Main | Pet | User | User-Pet |
|---|---|---|---|---|
| Route prefix | / | /pet | /user | /user/:user_id |
| View engine | Default | EJS | EJS | Default |
| Before middleware | No | Yes (load pet) | Yes (load user) | No |
| CRUD operations | None | Show, Edit, Update | List, Show, Edit, Update | Create |
| Custom prefix | No | No | No | Yes (/user/:user_id) |
The controllers demonstrate different aspects of the MVC pattern:
- Main controller shows a simple redirect
- Pet and User controllers show full CRUD with before middleware for resource loading
- User-Pet controller shows nested resource creation with custom route prefixing