Let's do a little more work setting up our SPA by building out some resourceful routing as part of a RESTFul API that we can connect later to our database and our client-side code. We're lucky that Express has such a vibrant community of developers building many add-ons, and we're going to use one for resourceful routing.
The first thing we need to do is to install our module, which will provide us with some resourceful routing:
npm install resource-routing -save
This installs the resourceful routing plugin we're going to use, and saves a reference to the package.json
file.
Next, we need to do some setup in our app.js
file:
var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var isJSON = require('./utils/json'); var routing = require('resource-routing'); var controllers = path.resolve('./controllers'); //Database stuff var mongodb = require('mongodb'); var monk = require('monk'); var db = monk('localhost:27017/giftapp') var routes = require('./routes/index'); var users = require('./routes/users'); var app = express(); routing.expose_routing_table(app, { at: ""/my-routes"" }); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); app.set('x-powered-by', false); app.locals.appName = ""My Gift App""; // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use(isJSON); //Database middlewear app.use(function(req,res,next){ req.db = db; next(); }); app.use('/', routes); app.use('/users', users); routing.resources(app, controllers, ""giftlist""); // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handlers // development error handler // will print stacktrace if (app.get('env') === 'development') { app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: err }); }); } // production error handler // no stacktraces leaked to user app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: {} }); }); module.exports = app;
We pull in the resource routing module using require()
and assign it to the variable routing
. Then we create a shortcut variable to a controllers directory, which we will be building next.
We add the following code, routing.expose_routing_table(app,{at:""/my-routes""});
which allows us to view our routing table at the URL my-routes
. Obviously, this is not something we'd leave intact in production, but it's a useful debugging tool.
Finally, we set up our resourceful routing for giftlists
with routing.resources(app,controllers,""giftlist"");
. This won't do anything yet because we haven't set up our controller.
By default, our resourceful router will build a number of standard restful routes for us, including:
GET /giftlist giftlist_controller.index GET /giftlist.format giftlist_controller.index GET /giftlist/new giftlist_controller.new GET /giftlist/new.format giftlist_controller.new POST /giftlist giftlist_controller.create POST /giftlist:format giftlist_controller.create GET /giftlist/:id giftlist_controller.show GET /giftlist/:id.format giftlist_controller.show GET /giftlist/:id/edit giftlist_controller.edit GET /giftlist/:id/edit.format giftlist_controller.edit PUT /giftlist/:id giftlist_controler.update PUT /giftlist/:id.format giftlist_controller.update DELETE /giftlist/:id giftlist_controller.destroy DELETE /giftlist/:id.format giftlist_controller.destroy
As you can see, these routes provide use with basic CRUD (create, read, update, delete) functionality.
However, these routes will only be created if the controller and routes actually exist, so we need to build them. Create a controllers directory in your giftapp
folder with a file called giftlist_controller.js
. Our plugin will add the _controller
part when it goes to load our controller, so be sure to name it correctly. For now, we are going to stub out our routes to make sure they are working:
exports.index = function(req, res){ res.send('giftlist index'); }; exports.new = function(req, res){ res.send('new giftlist'); }; exports.create = function(req, res){ res.send('create giftlist'); }; exports.show = function(req, res){ res.send('show giftlist'+ req.params.id); }; exports.edit = function(req, res){ res.send('edit giftlist'); }; exports.update = function(req, res){ res.send('update giftlist'); }; exports.destroy = function(req, res){ res.send('destroy giftlist'); };
As you can see, each of our route handlers is a function that receives the request and response objects.
Restart your server and navigate to localhost:3000/giftlist/17
, where you should see:
show giftlist 17
Our resourceful routes can also support different data formats, so let's stub those out as well, and we'll also use our isJSON
property in our giftlist_controller.js
:
exports.index = function(req, res){ if(req.params.format == ""json"" || req.isJSON){ res.json({""title"":""giftlist index""}) }else{ res.send('<h1>giftlist index</h1>'); } }; exports.new = function(req, res){ exports.index = function(req, res){ if(req.params.format == ""json"" || req.isJSON){ res.json({""title"":""new giftlist""}) }else{ res.send('<h1>new giftlist</h1>'); } }; }; exports.create = function(req, res){ exports.index = function(req, res){ if(req.params.format == ""json"" || req.isJSON){ res.json({""title"":""create giftlist""}) }else{ res.send('<h1>create giftlist</h1>'); } }; }; exports.show = function(req, res){ exports.index = function(req, res){ if(req.params.format == ""json"" || req.isJSON){ res.json({ ""title"":""show giftlist"", ""giftlist"":req.params.id }) }else{ res.send('<h1>show giftlist' + req.params.id + '</h1>'); } }; }; exports.edit = function(req, res){ exports.index = function(req, res){ if(req.params.format == ""json"" || req.isJSON){ res.json({ ""title"":""edit giftlist"", ""giftlist"":req.params.id }) }else{ res.send('<h1>edit giftlist' + req.params.id + '</h1>'); } }; }; exports.update = function(req, res){ exports.index = function(req, res){ if(req.params.format == ""json"" || req.isJSON){ res.json({ ""title"":""update giftlist"", ""giftlist"":req.params.id }) }else{ res.send('<h1>update giftlist' + req.params.id + '</h1>'); } }; }; exports.destroy = function(req, res){ exports.index = function(req, res){ if(req.params.format == ""json"" || req.isJSON){ res.json({ ""title"":""delete giftlist"", ""giftlist"":req.params.id }) }else{ res.send('<h1>delete giftlist' + req.params.id + '</h1>'); } }; };
Here, we added tests to each of our routes to see if the client is requesting JSON
data. If they are, we return JSON
. Otherwise, we return HTML
.
We check to see if the client is expecting JSON
in two ways.
First, we look to see if the req.params.format
is json
. Using this resourceful routing middleware, appending a .:format to the URL adds that format to the req.params
object as the value of the format. In other words, entering the URL localhost:3000/giftlist.json
triggers the giftlist_controller.index
route, setting the format parameter to json
.
The second method is to rely on the req.isJSON
parameter set by our middleware.
In the next chapter, we will connect these resourceful routes to CRUD operations on our database, and start to render data to a page as we flesh out our SPA.
18.222.182.73