Traducción en curso

En este primer articulo de Express resolveremos las preguntas "¿Qué es Node?" y "¿Qué es Express?", y te daremos una visión general de que hace especial al framework web "Express". Delinearemos las características principales, y te mostraremos algunos de los principales bloques de construcción de una aplicación en Express (aunque en este punto no tendrás todavía un entorno de desarrollo en que probarlo).

Pre-requisitos:

Conocimientos básicos de informática. Noción general sobre programación lado servidor de sitios web, y en particular los mecanismos de las interacciones cliente-servidor en sitios web.

Objetivo:

Ganar familiaridad con lo que es Express y cómo encaja con Node, qué funcionalidad proporciona y los pilares de construcción de una aplicación Express.

¿Qué son Express y Node?

Node (o más correctamente: Node.js) es un entorno que trabaja en tiempo de ejecución, de código abierto, multi-plataforma, que permite a los desarrolladores crear toda clase de herramientas de lado servidor y aplicaciones en JavaScript. La ejecución en tiempo real está pensada para usarse fuera del contexto de un explorador web (es decir, ejecutarse directamente en una computadora o sistema operativo de servidor). Como tal, el entorno omite las APIs de JavaScript específicas del explorador web y añade soporte para APIs de sistema operativo más tradicionales que incluyen HTTP y bibliotecas de sistemas de ficheros.

Desde una perspectiva de desarrollo de servidor web, Node tiene un gran número de ventajas:

  • ¡Gran rendimiento! Node ha sido diseñado para optimizar el rendimiento y la escalabilidad en aplicaciones web y es un muy buen complemento para muchos problemas comunes de desarrollo web (ej, aplicaciones web en tiempo real).
  • El código está escrito en "simple JavaScript", lo que significa que se pierde menos tiempo ocupándose de las "conmutaciones de contexto" entre lenguajes cuando estás escribiendo tanto el código del explorador web como del servidor.
  • JavaScript es un lenguaje de programación relativamente nuevo y se beneficia de los avances en diseño de lenguajes cuando se compara con otros lenguajes de servidor web tradicionales (ej, Python, PHP, etc.) Muchos otros lenguajes nuevos y populares se compilan/convierten a JavaScript de manera que puedes también usar CoffeeScript, ClosureScript, Scala, LiveScript, etc.
  • El gestor de paquetes de Node (NPM del inglés: Node Packet Manager) proporciona acceso a cientos o miles de paquetes reutilizables. Tiene además la mejor en su clase resolución de dependencias y puede usarse para automatizar la mayor parte de la cadena de herramientas de compilación.
  • Es portable, con versiones que funcionan en Microsoft Windows, OS X, Linux, Solaris, FreeBSD, OpenBSD, WebOS, y NonStop OS. Además, está bien soportado por muchos de los proveedores de hospedaje web, que proporcionan infraestructura específica y documentación para hospedaje de sitios Node.
  • Tiene un ecosistema y comunidad de desarrolladores de terceros muy activa, con cantidad de gente deseosa de ayudar.

Puedes crear de sencilla un servidor web básico para responder cualquier petición simplemente usando el paquete HTTP de Node, como se muestra abajo. Este, creará un servidor y escuchará cualquier clase de peticiones en la URL http://127.0.0.1:8000/; cuando se reciba una petición, se responderá enviando en texto la respuesta: "Hola Mundo!".

// Se carga el módulo de HTTP
var http = require("http");

// Creación del servidor HTTP, y se define la escucha
// de peticiones en el puerto 8000
http.createServer(function(request, response) {

   // Se define la cabecera HTTP, con el estado HTTP (OK: 200) y el tipo de contenido
   response.writeHead(200, {'Content-Type': 'text/plain'});
   
   // Se responde, en el cuerpo de la respuesta con el mensaje "Hello World"
   response.end('Hola Mundo!\n');
}).listen(8000);

// Se escribe la URL para el acceso al servidor
console.log('Servidor en la url http://127.0.0.1:8000/');

Otras tareas comunes de desarrollo web no están directamente soportadas por el mismo Node. Si quieres añadir el manejo específico de diferentes verbos HTTP (ej, GET, POST, DELETE, etc.), gestionar de forma separada las peticiones por medio de diferentes direcciones URL ("rutas"), servir ficheros estáticos o usar plantillas para crear la respuesta de forma dinámica, necesitarás escribir el código por tí mismo, o ¡puedes evitar reinventar la rueda usando un framework web!

Express es el framework web más popular de Node, y es la librería subyacente para un gran número de otros frameworks web de Node populares. Proporciona mecanismos para:

  • Escritura de manejadores de peticiones con diferentes verbos HTTP en diferentes caminos URL (rutas).
  • Integración con motores de renderización de "vistas" para generar respuestas mediante la introducción de datos en plantillas.
  • Establecer ajustes de aplicaciones web como qué puerto usar para conectar, y la localización de las plantillas que se utilizan para renderizar la respuesta.
  • Añadir procesamiento de peticiones "middleware" adicional en cualquier punto dentro de la tubería de manejo de la petición.

A pesar de que Express es en sí mismo bastante minimalista, los desarrolladores han creado paquetes de middleware compatibles para abordar casi cualquier problema de desarrollo web. Hay librerías para trabajar con cookies, sesiones, inicios de sesión de usuario, parámetros URL, datos POST, cabeceras de seguridad y muchos más. Puedes encontrar una lista de paquetes middleware mantenida por el equipo de Express en Express Middleware (junto con una lista de algunos de los paquetes más populares de terceros).

Nota: esta flexibilidad es una espada de doble filo. Hay paquetes de middleware para abordar casi cualquier problema o requerimiento, pero deducir cuáles son los paquetes adecuados a usar algunas veces puede ser un auténtico reto. Tampoco hay una "forma correcta" de estructurar una aplicación, y muchos ejemplos que puedes encontrar en la Internet no son óptimos, o sólo muestran una pequeña parte de lo que necesitas hacer para desarrollar una aplicación web.

¿Dónde comenzó?

Node fué lanzado inicialmente, sólo para Linux, en 2009. El gestor de paquetes NPM fué lanzado en 2010, y el soporte nativo para Windows fue añadido en 2012. La versión actual LTS (Long Term Suppport) es Node v8.9.3 mientras que la última versión es Node 9. Ésto es sólo una pequeña foto de una historia muy rica; profundiza en Wikipedia si quieres saber más).

Express fue lanzado inicialmente en Noviembre de 2010 y está ahora en la versión 4.16 de la API. Puedes comprobar en el changelog la información sobre cambios en la versión actual, y en GitHub notas de lanzamiento históricas más detalladas.

¿Qué popularidad tiene Node/Express?

La popularidad de un framework web es importante porque es un indicador de se continuará manteniendo y qué recursos tienen más probabilidad de estar disponibles en términos de documentación, librerías de extensiones y soporte técnico.

No existe una medida disponible de inmediato y definitiva de la popularidad de los frameworks de lado servidor (aunque sitios como Hot Frameworks intentan asesorar sobre popularidad usando mecanismos como contar para cada plataforma el número de preguntas sobre proyectos en GitHub y StackOverflow). Una pregunta mejor es si Node y Express son lo "suficientemente populares" para evitar los problemas de las plataformas menos populares. ¿Continúan evolucionando? ¿Puedes conseguir la ayuda que necesitas? ¿Hay alguna posibilidad de que consigas un trabajo remunerado si aprendes Express?

De acuerdo con el número de compañías de perfil alto que usan Express, el número de gente que contribuye al código base, y el número de gente que proporciona soporte tanto libre como pagado, podemos entonces decir que sí, !Express es un framework popular!

¿Es Express dogmático?

Los frameworks web frecuentemente se refieren a sí mismos como "dogmáticos" ("opinionated") o "no dogmáticos" ("unopinionated").

Los frameworks dogmáticos son aquellos que opinan acerca de la "manera correcta" de gestionar cualquier tarea en particular. Ofrecen soporte para el desarrollo rápido en un dominio en particular (resolver problemas de un tipo en particular) porque la manera correcta de hacer cualquier cosa está generalmente bien comprendida y bien documentada. Sin embargo pueden ser menos flexibles para resolver problemas fuera de su dominio principal, y tienden a ofrecer menos opciones para elegir qué componentes y enfoques pueden usarse.

Los framewoks no dogmáticos, en contraposición, tienen muchas menos restricciones sobre el modo mejor de unir componentes para alcanzar un objetivo, o incluso qué componentes deberían usarse. Hacen más fácil para los desarrolladores usar las herramientas más adecuadas para completar una tarea en particular, si bien al coste de que necesitas encontrar esos componentes por tí mismo.

Express es no dogmático, transigente. Puedes insertar casi cualquier middleware compatible que te guste dentro de la cadena de manejo de la petición, en casi cualquier orden que te apetezca. Puedes estructurar la app en un fichero o múltiples ficheros y usar cualquier estructura de directorios. ¡Algunas veces puedes sentir que tienes demasiadas opciones!

¿Cómo es el código para Express?

En sitios web o aplicaciones web dinámicas, que accedan a bases de datos, el servidor espera a recibir peticiones HTTP del navegador (o cliente). Cuando se recibe una petición, la aplicación determina cuál es la acción adecuada correspondiente, de acuerdo a la estructura de la URL y a la información (opcional) indicada en la petición con los métodos POSTGET. Dependiendo de la acción a realizar, puede que se necesite leer o escribir en la base de datos, o realizar otras acciones necesarias para atender la petición correctamente. La aplicación ha de responder al navegador, normalmente, creando una página HTML dinámicamente para él, en la que se muestre la información pedida, usualmente dentro de un elemento especifico para este fin, en una plantilla HTML.

Express posee métodos para especificar que función ha de ser llamada dependiendo del verbo HTTP usado en la petición (GET, POST, SET, etc.) y la estructura de la URL ("ruta"). También tiene los métodos para especificar que plantilla ("view") o gestor de visualización utilizar, donde están guardadas las plantillas de HTML que han de usarse  y como  generar la visualización adecuada para cada caso. El middleware de Express, puede usarse también para añadir funcionalidades para la gestión de cookies, sesiones y usuarios, mediante el uso de parámetros, en los métodos POST/GET.  Puede utilizarse además cualquier sistema de trabajo con bases de datos, que sea soportado por Node (Express no especifica ningún método preferido para trabajar con bases de datos). 

En las siguientes secciones, se explican algunos puntos comunes que se pueden encontrar cuando se trabaja con código de Node y Express.

Hola Mundo! - en Express

Primero consideremos el tradicional ejemplo de Hola Mundo! (se comentará cada parte a continuación).

Consejo: Si tiene Node y Express instalado (o piensa instalarlos posteriormente) puede guardar este código en un archivo llamado app.js y ejecutarlo posteriormente en la linea de comandos invocándolo mediante: node app.js

var express = require('express');
var app = express();

app.get('/', function(req, res) {
  res.send('Hola Mundo!');
});

app.listen(3000, function() {
  console.log('Aplicación ejemplo, escuchando el puerto 3000!');
});

Las primeras dos líneas incluyen (mediante la orden require()) el módulo de Express y crean una aplicación de Express. Este elemento se denomina comúnmente app, y posee métodos para el enrutamiento de las peticiones HTTP, configuración del 'middleware', y visualización de las vistas de HTM, uso del motores de 'templates', y gestión de las configuraciones de las aplicaciones  que controlan la aplicación (por ejemplo el entorno, las definiciones para enrutado ... et cetera.)

Las líneas que siguen en el código (las tres líneas que comienzan con app.get) muestran una definición de ruta que se llamará cuando se reciba una petición  HTTP GET con una dirección ('/') relativa al directorio raíz. La función 'callback' coge una petición y una respuesta como argumentos, y ejecuta un send() en la respuesta, para enviar la cadena  de caracteres: "Hola Mundo!".

El bloque final de código, define y crea el servidor, escuchando el puerto 3000 e imprime un comentario en la consola. Cuando se está ejecutando el servidor, es posible ir hasta la dirección  localhost:3000  en un navegador, y ver como el servidor de este ejemplo devuelve el mensaje de respuesta.

Importando y creando módulos

Un modulo es una librería o archivo JavaScript que puede ser importado dentro de otro código utilizando la función require() de Node.  Por sí mismo, Express es un modulo,  como lo son el middleware y las librerías de bases de datos que se utilizan en las aplicaciones Express.

El código mostrado abajo, muestra como puede importarse un modulo con base a su nombre, como ejemplo se utiliza el  framework Express . Primero se invoca la función require(), indicando como parámetro el nombre del módulo o librería como una cadena ('express'),  posteriormente se invoca el objeto obtenido para crear una aplicación Express.

Posteriormente, se puede acceder a las propiedades y funciones del objeto Aplicación.

var express = require('express');
var app = express();

También podemos crear nuestros propios módulos que puedan posteriormente ser importados de la misma manera.

Consejo:  Usted puede desear crear sus propios módulos, esto le permitirá organizar su código en partes más administrables; una aplicación que reside en un solo archivo es difícil de entender y manejar.

El utilizar módulos independientes también le permite administrar el espacio de nombres, de esta manera unicamente las variables que exporte explícitamente son importadas cuando utilice un módulo.

Para hacer que los objetos este disponibles fuera de un modulo, solamente es necesario asignarlos al objeto exports. Por ejemplo, el modulo mostrado a continuación square.js  es un archivo que exporta los métodos area() y perimeter() :

exports.area = function(width) { return width * width; };
exports.perimeter = function(width) { return 4 * width; };

Nosotros podemos importar este módulo utilizando la función require(), y entonces podremos invocar los métodos exportados de la siguiente manera:

// Utilizamos la función require() El nombre del archivo se ingresa sin la extensión (opcional) .js 
var square = require('./square');
// invocamos el metodo area()
console.log('El área de un cuadrado con lado de 4 es ' + square.area(4));

Nota: Usted también puede especificar una ruta absoluta a la  ubicación del módulo  (o un nombre como se realizó inicialmente). 

Si usted desea exportar completamente un objeto en una asignación en lugar de construir cada propiedad por separado, debe asignarlo al módulo module.exports como se muestra a continuación (también puede hacer esto al inicio de un constructor o de otra función.)

module.exports = {
  area: function(width) {
    return width * width;
  },
       
  perimeter: function(width) {
    return 4 * width;
  }
};

Para más información acerca de módulos vea Modulos (Node API docs).

Usando APIs asíncronas

El código JavaScript usa frecuentemente APIs asíncronas antes que sincrónicas para operaciones que tomen algún tiempo en completarse. En una API sincrónica cada operación debe completarse antes de que la siguiente pueda comenzar. Por ejemplo, la siguiente función de registro es síncrona, y escribirá en orden el texto en la consola (Primero, Segundo).

console.log('Primero');
console.log('Segundo');

En contraste, en una API asincrónica, la API comenzara una operación e inmediatamente retornará (antes de que la operación se complete). Una vez que la operación finalice, la API usara algún mecanismo para realizar operaciones adicionales. Por ejemplo, el código de abajo imprimirá "Segundo, Primero" porque aunque el método setTimeout() es llamado primero y retorna inmediatamente, la operación no se completa por varios segundos.

setTimeout(function() {
   console.log('Primero');
   }, 3000);
console.log('Segundo');

Usar APIs asíncronas sin bloques es aun mas importante en Node que en el navegador, porque Node es un entorno de ejecución controlado por eventos de un solo hilo. "Un solo hilo" quiere decir que todas las peticiones al servidor son ejecutadas en el mismo hilo ( en vez de dividirse en procesos separados). Este modelo es extremadamente eficiente en términos de velocidad y recursos del servidor, pero eso significa que si alguna de sus funciones llama a métodos sincrónicos que tomen demasiado tiempo en completarse, bloquearan no solo la solicitud actual, sino también cualquier otra petición que este siendo manejada por tu aplicación web.

Hay muchas maneras para una API sincrónica de notificar a su aplicación que se ha completado. La manera mas común es registrar una función callback cuando usted invoca a una API asincrónica, la misma sera llamada de vuelta cuando la operación se complete. Este es el enfoque utilizado anteriormente.

Tip: Usar "callbacks" puede ser un poco enmarañado si usted tiene una secuencia de operaciones asíncronas dependientes que deben ser llevadas a cabo en orden, porque esto resulta en múltiples niveles de "callbacks" anidadas. Este problema es comúnmente conocido como "callback hell" (callback del infierno). Este problema puede ser reducido con buenas practicas de código (vea http://callbackhell.com/), usando un modulo como async, o incluso avanzando a características de ES6 como las promesas.

Nota: Una convención común para Node y Express es usar callbacks de error primero. En esta convención el primer valor en su función callback es un error, mientras que los argumentos subsecuentes contienen datos correctos. Hay una buena explicación de porque este enfoque es útil en este blog: The Node.js Way - Understanding Error-First Callbacks (fredkschott.com).

Creando manejadores de rutas

En nuestro ejemplo anterior de "Hola Mundo!" en Express (véase mas arriba), definimos una función (callback) manejadora de ruta para peticiones HTTP GET a la raíz del sitio ('/').

app.get('/', function(req, res) {
  res.send('Hello World!');
});

La función callback toma una petición y una respuesta como argumentos. En este caso el método simplemente llama a send() en la respuesta para retornar la cadena "Hello World!". Hay un numero de otros métodos de respuesta para finalizar el ciclo de solicitud/respuesta, por ejemplo podrá llamar a res.json() para enviar una respuesta JSON o res.sendFile() para enviar un archivo.

JavaScript tip: Usted puede utilizar cualquier nombre que quiera para los argumentos en las funciones callback; cuando la callback es invocada el primer argumento siempre sera la petición y el segundo siempre sera la respuesta. Tiene sentido nombrarlos de manera que pueda identificar el objeto con el que esta trabajando en el cuerpo de la callback.

El objeto que representa una aplicación de Express, también posee métodos para definir los manejadores de rutas para el resto de los verbos HTTP: post(), put(), delete(), options(), trace(), copy(), lock(), mkcol(), move(), purge(), propfind(), proppatch(), unlock(), report(), mkactivity(), checkout(), merge(), m-search(), notify(), subscribe(), unsubscribe(), patch(), search(), y connect().

Hay un método general para definir las rutas:  app.all(), el cual será llamado en respuesta a cualquier método HTTP. Se usa para cargar funciones del middleware en una dirección particular para todos los métodos de peticiones. El siguiente ejemplo (de la documentación de Express) muestra el uso de los manejadores a  /secret  sin tener en cuenta el verbo HTTP utilizado (siempre que esté definido por el módulo http).

app.all('/secret', function(req, res, next) {
  console.log('Accediendo a la seccion secreta ...');
  next(); // pasa el control al siguiente manejador
});

Las rutas le permiten igualar patrones particulares de caracteres en la URL, y extraer algunos valores de ella y pasarlos como parámetros al manejador de rutas (como atributo del objeto petición pasado como parámetro).

Usualmente es útil agrupar manejadores de rutas para una parte del sitio juntos y accederlos usando un prefijo de ruta en común. (Ej: un sitio con una Wiki podría tener todas las rutas relacionadas a dicha sección en un archivo y siendo accedidas con el prefijo de ruta /wiki/. En Express esto se logra usando el objeto express.Router. Ej: podemos crear nuestra ruta wiki en un módulo llamado wiki.js, y entonces exportar el objeto Router, como se muestra debajo:

// wiki.js - Modulo de rutas Wiki

var express = require('express');
var router = express.Router();

// Home page route
router.get('/', function(req, res) {
  res.send('Página de inicio Wiki');
});

// About page route
router.get('/about', function(req, res) {
  res.send('Acerca de esta wiki');
});

module.exports = router;

Nota: Agregar rutas al objeto Router es como agregar rutas al objeto app ( como se vio anteriormente).

Para usar el router en nuestro archivo app principal, necesitamos require() el módulo de rutas (wiki.js), entonces llame use() en la aplicación Express para agregar el Router al software intermediario que maneja las rutas. Las dos rutas serán accesibles entonces desde /wiki/ y /wiki/about/.

var wiki = require('./wiki.js');
// ...
app.use('/wiki', wiki);

Le mostraremos mucho más sobre como trabajar con rutas, y en particular, acerca de como usar el Router, más adelante en la sección Rutas y controladores .

Usando middleware

El "middleware" es ampliamente utilizado en las aplicaciones de Express: desde tareas para servir archivos estáticos, a la gestión de errores o la compresión de las respuestas HTTP. Mientras las funciones de enrutamiento, con el objeto express.Router, se encargan del ciclo petición-respuesta, al gestionar la respuesta adecuada al cliente, las funciones de middleware normalmente realizan alguna operación al gestionar una petición o respuesta y a continuación llaman a la siguiente función en la "pila", que puede ser otra función de middleware  u otra función de enrutamiento. El orden en el que las funciones de middleware son llamadas depende del desarrollador de la aplicación.

Nota: El middleware puede realizar cualquier operación hacer cambios a una petición, ejecutar código, realizar cambios a la petición o al objeto pedido, puede también finalizar el ciclo de petición-respuesta. Si no finaliza el ciclo debe llamar a la función next() para pasar el control de la ejecución a la siguiente función del middleware ( o a la petición quedaría esperando una respuesta ... ). 

La mayoría de las aplicaciones usan middleware desarrollado por terceras personas, para simplificar funciones habituales en el desarrollo web, como puede ser: gestión de cookies, sesiones, autentificado de usuarios, peticiones POST  y datos en JSON, registros de eventos, etc. Puede encontrar en el siguiente enlace una  lista de middleware mantenido por el equipo de Express (que también incluye otros paquetes populares de terceras partes). Las librerías de Express están disponibles con la aplicación NPM (Node Package Manager).

Para usar estas colecciones, primer  o ha de instalar la aplicación usando NPM. Por ejemplo para instalar el registro de peticiones HTTP morgan, se haría con el comando Bash:  

$ npm install morgan

Entonces podría llamar a la función use() en un objeto de aplicación Express para utilizar este middleware a su aplicación. 

var express = require('express');
var logger = require('morgan');
var app = express();
app.use(logger('dev'));
...

Note: Middleware and routing functions are called in the order that they are declared. For some middleware the order is important (for example if session middleware depends on cookie middleware, then the cookie handler must be added first). It is almost always the case that middleware is called before setting routes, or your route handlers will not have access to functionality added by your middleware.

You can write your own middleware functions, and you are likely to have to do so (if only to create error handling code). The only difference between a middleware function and a route handler callback is that middleware functions have a third argument next, which middleware functions are expected to call if they do not complete the request cycle (when the middleware function is called, this contains the next function that must be called).

You can add a middleware function to the processing chain with either app.use() or app.add(), depending on whether you want to apply the middleware to all responses or to responses with a particular HTTP verb (GET, POST, etc). You specify routes the same in both cases, though the route is optional when calling app.use().

The example below shows how you can add the middleware function using both methods, and with/without a route.

var express = require('express');
var app = express();

// An example middleware function
var a_middleware_function = function(req, res, next) {
  // ... perform some operations
  next(); // Call next() so Express will call the next middleware function in the chain.
}

// Function added with use() for all routes and verbs
app.use(a_middleware_function);

// Function added with use() for a specific route
app.use('/someroute', a_middleware_function);

// A middleware function added for a specific HTTP verb and route
app.get('/', a_middleware_function);

app.listen(3000);

JavaScript Tip: Above we declare the middleware function separately and then set it as the callback. In our previous route handler function we declared the callback function when it was used. In JavaScript, either approach is valid.

The Express documentation has a lot more excellent documentation about using and writing Express middleware.

Serving static files

You can use the express.static middleware to serve static files, including your images, CSS and JavaScript (static() is the only middleware function that is actually part of Express). For example, you would use the line below to serve images, CSS files, and JavaScript files from a directory named 'public' at the same level as where you call node:

app.use(express.static('public'));

Any files in the public directory are served by adding their filename (relative to the base "public" directory) to the base URL. So for example:

http://localhost:3000/images/dog.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/app.js
http://localhost:3000/about.html

You can call static() multiple times to serve multiple directories. If a file cannot be found by one middleware function then it will simply be passed on to the subsequent middleware (the order that middleware is called is based on your declaration order).

app.use(express.static('public'));
app.use(express.static('media'));

You can also create a virtual prefix for your static URLs, rather than having the files added to the base URL. For example, here we specify a mount path so that the files are loaded with the prefix "/media":

app.use('/media', express.static('public'));

Now, you can load the files that are in the public directory from the /media path prefix.

http://localhost:3000/media/images/dog.jpg
http://localhost:3000/media/video/cat.mp4
http://localhost:3000/media/cry.mp3

For more information, see Serving static files in Express.

Handling errors

Errors are handled by one or more special middleware functions that have four arguments, instead of the usual three: (err, req, res, next). For example:

app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

These can return any content required, but must be called after all other app.use() and routes calls so that they are the last middleware in the request handling process!

Express comes with a built-in error handler, which takes care of any remaining errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack. If you pass an error to next() and you do not handle it in an error handler, it will be handled by the built-in error handler; the error will be written to the client with the stack trace.

Note: The stack trace is not included in the production environment. To run it in production mode you need to set the the environment variable NODE_ENV to 'production'.

Note: HTTP404 and other "error" status codes are not treated as errors. If you want to handle these, you can add a middleware function to do so. For more information see the FAQ.

For more information see Error handling (Express docs).

Using databases

Express apps can use any database mechanism supported by Node (Express itself doesn't define any specific additional behaviour/requirements for database management). There are many options, including PostgreSQL, MySQL, Redis, SQLite, MongoDB, etc.

In order to use these you have to first install the database driver using NPM. For example, to install the driver for the popular NoSQL MongoDB you would use the command:

$ npm install mongodb

The database itself can be installed locally or on a cloud server. In your Express code you require the driver, connect to the database, and then perform create, read, update, and delete (CRUD) operations. The example below (from the Express documentation) shows how you can find "mammal" records using MongoDB.

var MongoClient = require('mongodb').MongoClient;

MongoClient.connect('mongodb://localhost:27017/animals', function(err, db) {
  if (err) throw err;

  db.collection('mammals').find().toArray(function (err, result) {
    if (err) throw err;

    console.log(result);
  });
});

Another popular approach is to access your database indirectly, via an Object Relational Mapper ("ORM"). In this approach you define your data as "objects" or "models" and the ORM maps these through to the underlying database format. This approach has the benefit that as a developer you can continue to think in terms of JavaScript objects rather than database semantics, and that there is an obvious place to perform validation and checking of incoming data. We'll talk more about databases in a later article.

For more information see Database integration (Express docs).

Rendering data (views)

Template engines (referred to as "view engines" by Express) allow you to specify the structure of an output document in a template, using placeholders for data that will be filled in when a page is generated. Templates are often used to create HTML, but can also create other types of document. Express has support for a number of template engines, and there is a useful comparison of the more popular engines here: Comparing JavaScript Templating Engines: Jade, Mustache, Dust and More.

In your application settings code you set the template engine to use and the location where Express should look for templates using the 'views' and 'view engines' settings, as shown below (you will also have to install the package containing your template library too!)

var express = require('express');
var app = express();

// Set directory to contain the templates ('views')
app.set('views', path.join(__dirname, 'views'));

// Set view engine to use, in this case 'some_template_engine_name'
app.set('view engine', 'some_template_engine_name');

The appearance of the template will depend on what engine you use. Assuming that you have a template file named "index.<template_extension>" that contains placeholders for data variables named 'title' and "message", you would call Response.render() in a route handler function to create and send the HTML response:

app.get('/', function(req, res) {
  res.render('index', { title: 'About dogs', message: 'Dogs rock!' });
});

For more information see Using template engines with Express (Express docs).

File structure

Express makes no assumptions in terms of structure or what components you use. Routes, views, static files, and other application-specific logic can live in any number of files with any directory structure. While it is perfectly possible to have the whole Express application in one file, typically it makes sense to split your application into files based on function (e.g. account management, blogs, discussion boards) and architectural problem domain (e.g. model, view or controller if you happen to be using an MVC architecture).

In a later topic we'll use the Express Application Generator, which creates a modular app skeleton that we can easily extend for creating web applications.

Summary

Congratulations, you've completed the first step in your Express/Node journey! You should now understand Express and Node's main benefits, and roughly what the main parts of an Express app might look like (routes, middleware, error handling, and template code). You should also understand that with Express being an unopinionated framework, the way you pull these parts together and the libraries that you use are largely up to you!

Of course Express is deliberately a very lightweight web application framework, so much of its benefit and potential comes from third party libraries and features. We'll look at those in more detail in the following articles. In our next article we're going to look at setting up a Node development environment, so that you can start seeing some Express code in action.

See also

In this module

Etiquetas y colaboradores del documento

 Última actualización por: Sergio_Gonzalez_Collado,