RepoFold

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 MethodHTTP VerbRoute PatternPurpose
listGET/{controller_name}List all resources
showGET/{controller_name}/:idShow a single resource
editGET/{controller_name}/:id/editShow edit form
updatePUT/{controller_name}/:idUpdate a resource
createPOST/{controller_name}Create a resource
indexGET/{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:

  1. Reads the controllers directory to discover all subdirectories
  2. For each subdirectory, requires its index.js file
  3. Inspects the exported methods of each controller
  4. Generates Express routes based on the method names and their corresponding HTTP verbs
  5. Applies any before middleware 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:

SymbolSignaturePurpose
indexfunction(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:

SymbolSignaturePurpose
engine'ejs'Specifies EJS as the view engine for this controller
beforefunction(req, res, next)Loads pet by ID from database, calls next('route') if not found
showfunction(req, res, next)Renders the show view with the loaded pet
editfunction(req, res, next)Renders the edit view with the loaded pet
updatefunction(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:

SymbolSignaturePurpose
engine'ejs'Specifies EJS as the view engine for this controller
beforefunction(req, res, next)Loads user by ID from database, calls next('route') if not found
listfunction(req, res, next)Renders the list view with all users
showfunction(req, res, next)Renders the show view with the loaded user
editfunction(req, res, next)Renders the edit view with the loaded user
updatefunction(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:

SymbolSignaturePurpose
name'pet'Specifies the resource name for route generation
prefix'/user/:user_id'Custom route prefix for nested resource
createfunction(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

AspectMainPetUserUser-Pet
Route prefix//pet/user/user/:user_id
View engineDefaultEJSEJSDefault
Before middlewareNoYes (load pet)Yes (load user)No
CRUD operationsNoneShow, Edit, UpdateList, Show, Edit, UpdateCreate
Custom prefixNoNoNoYes (/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
Generated from commit ae6dd37