Quite obviously, you’re going to need node.js and mongodb installed. I recommend downloading and installing from the node.js website and following the instructions.
I will note that this tutorial covers
[~/local/src/coffee]$ express --version 4.11.2so if you come across this post a year from now (2015) the API might have changed significantly since then.
You will also need mongodb installed.
Finally, since we’ll be using coffeescript for this tutorial, run
[~/local/src/coffee]$ npm install -g coffee-script npm http GET https://registry.npmjs.org/coffee-script npm http 200 https://registry.npmjs.org/coffee-script /usr/local/bin/coffee -> /usr/local/lib/node_modules/coffee-script/bin/coffee /usr/local/bin/cake -> /usr/local/lib/node_modules/coffee-script/bin/cake coffee-script@1.9.1 /usr/local/lib/node_modules/coffee-script [~/local/src/coffee]$ coffee --version CoffeeScript version 1.9.1(you might need to sudo) to install coffeescript.
Run coffee from the commandline to access the coffeescript REPL. If all works well, install these additional packages listed below via npm that we’ll be using throughout the tutorial.
Now let’s bootstrap our project structure. Type
express coffeepressto generate a skeleton express project structure.
[~/local/src/coffee]$ express coffeepress create : coffeepress create : coffeepress/package.json create : coffeepress/app.js create : coffeepress/public create : coffeepress/public/images create : coffeepress/public/javascripts create : coffeepress/public/stylesheets create : coffeepress/public/stylesheets/style.css create : coffeepress/routes create : coffeepress/routes/index.js create : coffeepress/routes/users.js create : coffeepress/views create : coffeepress/views/index.jade create : coffeepress/views/layout.jade create : coffeepress/views/error.jade create : coffeepress/bin create : coffeepress/bin/www install dependencies: $ cd coffeepress && npm install run the app: $ DEBUG=coffeepress:* ./bin/www
You should see output similar to the following:
[~/local/src/coffee]$ cd coffeepress [~/local/src/coffee/coffeepress]$ tree . |--- app.js |--- bin | `-- www |--- package.json |--- public | |--- images | |--- javascripts | `-- stylesheets | `-- style.css |--- routes | |--- index.js | `-- users.js `-- views |--- error.jade |--- index.jade `-- layout.jade 7 directories, 9 files
The idea is simple: most projects require a certain amount of so-called boilerplate code, and who wants to recreate that code every time you begin a new project? A simple way is to create a rough skeleton of a project, and every time you need a new project, you just copy this skeleton, or template.
Express has taken a page from Ruby on Rails and provided the express
utility to
generate scaffolding to start your Express project.
[~/src/coffee]$ express --help Usage: express [options] [dir] Options: -h, --help output usage information -V, --version output the version number -e, --ejs add ejs engine support (defaults to jade) --hbs add handlebars engine support -H, --hogan add hogan.js engine support -c, --css <engine> add stylesheet <engine> support (less|stylus|compass) (defaults to plain css) --git add .gitignore -f, --force force on non-empty directory
Boilerplate is also useful for the actual HTML that will be delivered to the client. See for example https://html5boilerplate.com/.
There are three folders in the root:
app.use(express.static(path.join(__dirname, 'public')));The static middleware allows you to designate one or more directories as containing static resources that are simply to be delivered to the client without any special handling. This is where you would put things like images, CSS files, and client-side JavaScript files.
|--- public | |--- images | |--- javascripts | `-- stylesheets | `-- style.css
| `-- views |--- error.jade |--- index.jade `-- layout.jadeWhere a view differs from a static resource (like an image or CSS file) is that a view doesn’t necessarily have to be static: the HTML can be constructed on the fly to provide a customized page for each request.
Express supports many different view engines that provide different levels of abstraction. Express gives some preference to a view engine called Jade
| |--- routes | |--- index.js | `-- users.js
The best thing about this structure is that it's easy to get started with and is known to most developers.
The package.json
file is automatically populated with
"private": true
in your
package.json
, then npm
will refuse to publish it.
This is a way to prevent accidental publication of private repositories.
[/tmp/coffeepress]$ cat package.json { "name": "coffeepress", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www" }, "dependencies": { "body-parser": "~1.10.2", "cookie-parser": "~1.3.3", "debug": "~2.1.1", "express": "~4.11.1", "jade": "~1.9.1", "morgan": "~1.5.1", "serve-favicon": "~2.2.0" }
req.body
·
req.cookies
with an object keyed by the cookie names.
The starting script is bin/www
which loads app.js
[/tmp/coffeepress]$ cat bin/www #!/usr/bin/env node /** * Module dependencies. */ var app = require('../app'); var debug = require('debug')('coffeepress:server'); var http = require('http'); /** * Get port from environment and store in Express. */ var port = normalizePort(process.env.PORT || '3000'); app.set('port', port); /** * Create HTTP server. */ var server = http.createServer(app); /** * Listen on provided port, on all network interfaces. */ server.listen(port); server.on('error', onError); server.on('listening', onListening); /** * Normalize a port into a number, string, or false. */ function normalizePort(val) { var port = parseInt(val, 10); if (isNaN(port)) { // named pipe return val; } if (port >= 0) { // port number return port; } return false; } /** * Event listener for HTTP server "error" event. */ function onError(error) { if (error.syscall !== 'listen') { throw error; } var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port // handle specific listen errors with friendly messages switch (error.code) { case 'EACCES': console.error(bind + ' requires elevated privileges'); process.exit(1); break; case 'EADDRINUSE': console.error(bind + ' is already in use'); process.exit(1); break; default: throw error; } } /** * Event listener for HTTP server "listening" event. */ function onListening() { var addr = server.address(); var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; debug('Listening on ' + bind); }
app.js
loads the middleware, assigns the route handlers, and starts the server.
[/tmp/coffeepress]$ cat app.js 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 routes = require('./routes/index'); var users = require('./routes/users'); var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); // uncomment after placing your favicon in /public //app.use(favicon(__dirname + '/public/favicon.ico')); app.use(logger('dev')); app.use(bodyParser.json()); // Returns middleware that only parses json /* URLs can only be sent over the Internet using the ASCII character-set. Since URLs often contain characters outside the ASCII set, the URL has to be converted into a valid ASCII format. */ app.use(bodyParser.urlencoded({ extended: false })); //The "extended" syntax allows for rich objects and arrays to be encoded app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', routes); app.use('/users', users); // 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;
app.use([path,] function [, function...])
mounts the middleware function(s) at the path.
If path is not specified, it defaults to "/"
.
Asi pues
var routes = require('./routes/index'); var users = require('./routes/users'); .... app.use('/', routes); app.use('/users', users);las rutas especificadas en
routes
quedan montadas en la raiz
y las rutas especificadas en users
quedan montadas en users/...
app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); });
Middleware is a way to encapsulate functionality: specifically, functionality that operates on an HTTP request to your application.
Practically, a middleware is simply a function that takes three arguments:
next()
, the pipeline will be terminated, and
no more route handlers or middleware will be processed.
next()
, you should send a response to the client
(res.send
, res.json
, res.render
, etc.);
if you don’t, the client will hang and eventually time out.
Routing is the mechanism by which requests (as specified by a URL and HTTP method) are routed to the code that handles them.
Instalamos las dependencias:
[~/src/coffee/coffeepress]$ npm install npm http GET https://registry.npmjs.org/jade ....y ejecutamos:
~/src/coffee/coffeepress]$ DEBUG=coffeepress:* ./bin/www coffeepress:server Listening on port 3000 +0msThen load
http://localhost:3000/
in your browser to access the app.
El generador de Express nos produjo el fichero bin/www
.
Este programa hace uso del módulo
debug
que es el que nos permite emitir el mensaje de Listening on
:
[~/src/coffee/coffeepress(master)]$ grep debug bin/www.coffee debug = require('debug')('coffeepress:server') debug 'Listening on ' + bindLa primera línea
debug = require('debug')('coffeepress:server')
carga la librería
y establece el nombre de www.coffee
a efectos de depuración como
coffeepress:server
.
The DEBUG
environment variable must be set to a list of file names
separated by commas or spaces. It is then used to enable these
debug('...')
messages.
Por ejemplo:
DEBUG=coffeepress:* bin/wwwdice que se activan los mensajes de debug en cualquier fichero con prefijo de nombre
coffeepress
.
Casiano Rodríguez León