Subsecciones

Getting Started

This is a tutorial on using to build a simple blog.

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.2
so 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.

Express generator

Now let’s bootstrap our project structure. Type

express coffeepress
to 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

This is called Scaffolding.

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/.

The Structure

There are three folders in the root:

Apart from these existing folders and the models folder, which we need to create ourselves, we might also create folders for

The best thing about this structure is that it's easy to get started with and is known to most developers.

package.json

The package.json file is automatically populated with

[/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"
  }

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

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;

El método app.use

mientras que este middleware queda asociado con la raiz:
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

Middleware

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:

Las Rutas / Routing

Routing is the mechanism by which requests (as specified by a URL and HTTP method) are routed to the code that handles them.

Running

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 +0ms
Then load http://localhost:3000/ in your browser to access the app.

El módulo debug

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 ' + bind
La 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/www
dice que se activan los mensajes de debug en cualquier fichero con prefijo de nombre coffeepress.

Casiano Rodríguez León
2016-03-27