Influencias / usos
Otros frameworks similares:
- Zend (PHP)
- Django (Python)
- Sinatra (Ruby)
Uso:
- APIs
- SPA (Single Page Application)
- Tiempo real
Pros:
- Rutas
- Parámetros
- Formularios y subida de ficheros
- Cookies
- Sesiones
- Templates
- Middlewares
Contras:
- Base de datos / ORM
- Autenticación de usuarios
- Seguridad
- Migraciones
- Deployment
- Organización del código
Migraciones:
Instalación
# Local
npm install express
# Global
npm install -g express
# Any version
npm install -g [email protected]
Hello World!
const express= require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(8080, () => console.log('Example app listening on port 8080'));
Mecánica: app.set()
- Nos permite establecer la configuración de Express
- Podemos almacenar datos personalizados de manera global
Ej. Guardando la versión de la aplicación
app.set('version', '1.5.0');
app.get('version'); // 1.5.0
app.enable('dia_soleado'); // igual a -> app.set('dia_soleado', true);
app.enabled('dia_soleado'); // igual a -> app.get('dia_soleado') === true;
app.disabled('dia_soleado'); // igual a -> app.get('dia_soleado') === false;
Ej. Sobreescribiendo variables del framework:
app.set('port', process.env.PORT || 3000);
Ej. Configuraciones según el entorno
NODE_ENV=production node app.js
app.set('enable-some-functionality', process.env.NODE_ENV === 'production');
El objeto locals
tanto de app
como de cada vista es el responsable de pasar las variables al motor de plantillas.
Variables locales a nivel de aplicación:
app.locals.title = 'My App';
app.locals.email = '[email protected]';
Variables locales a nivel de ruta:
app.get('/', (req, res) => {
res.render('index', {
title: 'Hey',
message: 'Hello there!'
});
});
Cambiando el motor de renderizado a pug
:
// npm install pug
const express = require('express');
const app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.get('/', (req, res) => {
res.locals.title = 'Nope...';
res.render('index', {
title: 'Login'
});
});
app.listen(8080);
Mecánica: app.all(), app.get(), app.post(), app.put(), app.delete(), app.route()
app[`METHOD`]('<route_name>', (request, response) => {});
Estructura:
app
: Aplicación de express o routerMETHOD
: Metodo HTTP de la Ruta- Soportados: get, post, put, head, delete, options, trace, copy, lock, mkcol, move, purge, propfind, proppatch, unlock, report, mkactivity, checkout, merge, m-search, notify, subscribe, unsubscribe, patch, search y connect.
- Para todas las rutas usamos app.all()
<route_name>
: Ruta (url) donde aplica- Cadenas de texto
- Patrones en cadenas de texto (Reducido a los subconjuntos:
?
,+
,*
, y()
) - Expresiones regulares
callback
: La función que será llamada cuando se alcance la ruta con el método correctos- Se pueden usar funciones
- Se pueden usar arrays de funciones
- Se pueden mezclar arrays y funciones
- Argumentos:
- Objeto
Request
de Node.js - Objeto
Response
de Node.js next()
: (Opcional) Función que dispara el siguiente middleware
- Objeto
Métodos HTTP: delimitando a un único método
app.get('/', (req, res, next) => {
res.send('Solo get como método me vale...');
});
Métodos HTTP: Otra forma de delimitar a un método (method-override)
app['m-search']('/', (req, res, next) => {
res.send('Solo m-search como método me vale...');
});
Métodos HTTP: permitiendo todos los métodos
app.all('/', (req, res, next) => {
res.send('Cualquier método me vale...');
});
Rutas: Index (http://localhost:8080
)
app.get('/', (req, res, next) => {
res.send('Esto es la Raiz');
});
Rutas: Básicas (http://localhost:8080/hola
)
app.get('/hola', (req, res, next) => {
res.send('Esto es /hola');
});
Rutas: Capturando Parámetros (http://localhost:8080/hola/Eduardo
, http://localhost:8080/hola/Oscar
, ...)
app.get('/hello/:nombre', (req, res) => {
res.send('Hola ' + req.params.nombre + '!');
});
Rutas: Capturando varios parámetros (http://localhost:8080/desde/Madrid/a/Malga
, http://localhost:8080/desde/Madrid/a/NYC
)
app.get('/desde/:origen/a/:destino', (req, res, next) => {
res.send('Quieres ir de ' + req.params.origen + ' a ' + req.params.destino);
});
Rutas: Capturando varios parámetros y alguno determiando (http://localhost:8080/mensaje/1/editar
, http://localhost:8080/mensaje/500/borrar
, ...)
app.get('/mensaje/:id/:action(editar|borrar)', (req,res,next) => {
res.send('Quieres ' + req.params.action + ' el mensaje numero ' + req.params.id);
});
Rutas: Parámetros opcionales http://localhost:8080/user/1/editar
, http://localhost:8080/user/500/borrar
, ...
app.get('/user/:id/:comando?', (req, res, next) => {
if(req.params.comando){
res.send("Tenemos algo! Quieres " + req.params.comando);
} else {
res.send("Nada de nada...");
}
});
Rutas: Más parámetros opcionales (http://localhost:8080/user/1.pdf
, http://localhost:8080/user/500.zip
, ...)
app.get('/user/:id.:formato?', (req, res, next) => {
if(req.params.formato){
res.send("["+req.params.formato+"] Extensión requerida... ");
} else {
res.send("Sin Extensión requerida");
}
});
Rutas: Tipo fichero (http://localhost:8080/hola.text
)
app.get('/hola.text', (req, res) => {
res.send('Hola');
});
Rutas: Patrones de serie (http://localhost:8080/acd
o http://localhost:8080/abcd*
)
app.get('/ab?cd', (req, res) => {
res.send('ab?cd');
});
Rutas: Patrones de serie (http://localhost:8080/abcd
, http://localhost:8080/abbbbbcd
, ...)
app.get('/ab+cd', (req, res) => {
res.send('ab+cd');
});
Rutas: Patrones de serie (http://localhost:8080/abcd
, http://localhost:8080/abAALGOOOcd
, ...)
app.get('/ab*cd', (req, res) => {
res.send('ab*cd');
});
Rutas: Patrones de serie (http://localhost:8080/abe
o http://localhost:8080/abcd*
)
app.get('/a(bc)d', (req, res) => {
res.send('a(bc)d');
});
Rutas: Expresiones regulares (http://localhost:8080/mcfly
, http://localhost:8080/dragonfly
, ...)
app.get(/.*fly$/, (req, res) => {
res.send('/.*fly$/');
});
Definiendo diferentes métodos para la misma ruta con app.route()
:
app.route('/pelicula')
.get((req, res) => {
res.send('todas las peliculas...');
})
.post((req, res) => {
res.send('Añadir una pelicula...');
})
Handlers: Una única función:
app.get('/example/a', (req, res) => {
res.send('Hola desde A!');
});
Handlers: Varias funciones:
app.get('/example/b', (req, res, next) => {
console.log('La respuesta se enviará a la siguiente función...');
next();
}, (req, res) => {
res.send('Hola desde B!');
});
Handlers: Arrays:
const cb0 = (req, res, next) => {
console.log('CB0');
next();
};
const cb1 = (req, res, next) => {
console.log('CB1');
next();
};
const cb2 = (req, res) => {
res.send('Hola desde C!');
};
app.get('/example/c', [cb0, cb1, cb2]);
Handlers: Arrays y funciones:
const cb0 = (req, res, next) => {
console.log('CB0');
next();
};
const cb1 = (req, res, next) => {
console.log('CB1');
next();
};
app.get('/example/d', [cb0, cb1], (req, res, next) => {
console.log('La respuesta se enviará a la siguiente función...');
next();
}, (req, res) => {
res.send('Hola desde D!');
});
req.ip
: Almacena la IP desde donde se realizó la peticioónreq.is
: Que tipo de datos nos llegan desde la petición. Booleano
req.is('json');
req.is('application/*');
req.is('application/json');
req.params
: Contenido de la ruta (http://localhost:8080/usuarios/:id)req.query
: Contenido de la consulta de la URL (http://localhost:8080/peliculas?categoria=Ficcion&director=George+Lucas)
app.get('/peliculas', function(req,res,next){
console.log(req.query.director) // George Lucas
console.log(req.query.categoria) // Ficcion
});
req.body
: Contenido dentro de la propia petición
- res.download() Solicita un archivo para descargarlo.
- res.end() Finaliza el proceso de respuesta.
- res.json() Envía una respuesta JSON.
- res.jsonp() Envía una respuesta JSON con soporte JSONP.
- Valores por defecto ajustables en app.set()
- ?callback= valor por defecto en la petición
res.jsonp({date: newDate()});
- Valores por defecto ajustables en app.set()
- res.redirect() Redirecciona una solicitud.
- res.render() Renderiza una plantilla a la que le pasa además datos (opcional)
- res.send() Envía una respuesta de varios tipos.
- Muy flexible
- Código y contenido
res.send(404,'Oops...');
- Enviar un JSON
res.send({mensaje: "secreto"});
- Solo código (autocompleta el mensaje)
res.send(200);
- Código y contenido
- Muy flexible
- res.sendFile() Envía un archivo para ser visualizado.
- res.sendStatus() Envía un archivo para ser descargado.
Es una función que recibe 3 o 4 parámetros: request
, response
, next
. Lo que hasta ahora llamábamos handler
.
Tipos:
- Middleware de nivel de aplicación
- Middleware de nivel de direccionador
- Middleware de manejo de errores
- Middleware incorporado
- Middleware de terceros
Siempre que queramos continuar la ejecución y captura de la petición HTTP, es necesario invocar a
next()
.
Mecánica: app.use():
app.use((req, res, next) => {
console.log("Petición en "+req.url+" con el método" + req.method);
next();
});
const express = require('express');
const app = express();
function logger(req, res, next) {
console.log(`Nueva petición en ${req.url} con el método ${req.method}`);
next();
};
app.use(logger);
app.get('/', (req, res) => {
res.send('Hola a todos!');
});
app.listen(3000);
Middleware de nivel de aplicación:
Global Afecta a cualquier Ruta
const app = express();
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
Punto de Montaje Afecta solo a una vía de acceso, en este caso /user/:id
const app = express();
app.get('/user/:id', (req, res, next) => {
console.log('ID:', req.params.id);
next();
}, (req, res, next) => {
res.send('User Info');
});
app.param('user_id', (req, res, next, id) => {
// Llamamos a una función que valida....
someValidateUserFunction(id, (err, usuario) => {
if (err) {
next(err);
} else if (user) {
req.user = user
next();
} else {
next(new Error('No user found with given ID'));
}
});
});
app.get('/user/:user_id', function (req, res, next) {
// req.user contains the middleware value
next()
})
const app = express();
const router = express.Router();
const USERS = [{
id: 0,
name: 'Jose'
}];
router.get('/user/:id', (req, res, next) => {
res.send(USERS.find(({ id }) => id === req.params.id));
});
router.get('/users', (req, res, next) => {
res.send(USERS);
});
app.use('api/v01', router);
const bodyParser = require('body-parser');
const methodOverride = require('method-override');
function serverError(err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' });
} else {
res.status(500);
res.render('error', { error: err });
}
}
app.use(serverError);
Desde la versión 4.x Express no depende de Connect Solamente queda incorporado express.static
Incluyendo archivos estáticos:
app.use(express.static('public'));
Configurando la carpeta pública:
const options = {
dotfiles: 'ignore',
etag: false,
extensions: ['htm', 'html'],
index: false,
maxAge: '1d',
redirect: false,
setHeaders(res, path, stat) {
res.set('x-timestamp', Date.now());
}
}
app.use(express.static('public', options));
Usando múltiples directorios estáticos:
app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));
Middleware oficial (No incorporado):
- serve-favicon
- Sirve el favicon
- Morgan
- Logger para peticiones HTTP
- body-parser
- Decodifica:
- application/json
- application/x-www-form-urlencoded
- multipart/form-data
- Crea el objeto req.body con los parámetros
- Crea el objeto req.files con los ficheros que se han subido desde un formulario
- Decodifica:
- basic-auth-connect
- Protección básica de las rutas usando usuario y contraseña
- csurf
- Crea req.session._csrf
- Protección contra Cross-site request forgery
- Usando tokens
- cors
- Gestión de Cross Origin Resource Sharing (CORS)
- compression
- express-session
- Simple gestor de sesiones
- multer
- Node.js middleware for handling
multipart/form-data
.
- Node.js middleware for handling
- cookie-session
- Simple cookie-based session middleware
- cookie-parser
- cookie parsing middleware
- Crea req.cookies
- cookie-session
- Inicializa y parsea los datos de sesión del usuario
- Utilizando cookies como almacenamiento
- Algunas opciones avanzadas
- serve-static
- Serve static files
- ¡Muy útil! Se pone cerca del final
- Cachea los ficheros
- La variable global
__dirname
contiene el directorio donde reside el script en ejecución
- vhost
- Virtual Domain Hosting
- restful-router
- Simple RESTful url router.
- connect-markdown
- Auto convert markdown to html for connect.
Habilitando CORS:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
Baneando Navegadores (IE8 y anteriores):
// ban.js
const banned = [ 'MSIE', 'Trident/6.0'];
module.exports = () => (req, res, next) =>{
if (req.headers['user-agent'] !== undefined && req.headers['user-agent'].indexOf(banned) > -1) {
res.end('Navegador no compatible');
} else {
next();
}
};
Redireccionando al usuario en caso de no estar autenticados:
module.exports = (req, res, next) => {
if (req.params.usuario.logged){
next();
} else {
res.redirect('/login');
}
};
El generador de aplicaciones se utiliza para crear rápidamente un esqueleto de aplicación.
npm install express-generator -g
# Se puede generar el proyecto utilizando "npx"
npx express-generator <project_name>
Generar un proyecto (genera un directorio)
express <project_name>
Entramos en la carpeta e instalamos las dependencias
cd <nombre_proyecto> && npm install
├── app.js (Nuestra aplicación - módulo)
├── bin (Gestión de la aplicación)
│ └── www
├── package.json (Información y dependencias)
├── public (Nuestros estáticos)
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes (Nuestros controladores)
│ ├── index.js
│ └── users.js
└── views (Nuestras vistas/plantillas)
├── error.jade
├── index.jade
└── layout.jade
# Window
set DEBUG=<nombre_proyecto>:* & npm start
# UNIX
DEBUG=<nombre_proyecto>:* npm start
Jade... ya no se llama jade. Ahora se llama PUG
Entendiendo la mécanica:
index.pug
doctype html
html(lang="en")
head
title= pageTitle
script(type='text/javascript').
if (foo) {
bar(1 + 5)
}
body
h1 Jade - node template engine
#container.col
if youAreUsingJade
p You are amazing
else
p Get on it!
p.
Jade is a terse and simple
templating language with a
strong focus on performance
and powerful features.
Se transforma en index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Jade</title>
<script type="text/javascript">
if (foo) {
bar(1 + 5)
}
</script>
</head>
<body>
<h1>Jade - node template engine</h1>
<div id="container" class="col">
<p>You are amazing</p>
<p>
Jade is a terse and simple
templating language with a
strong focus on performance
and powerful features.
</p>
</div>
</body>
</html>
Scripts:
script(src='//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js')
script(src='//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js')
1 - Crearemos una aplicación utilizando Express para gestionar las películas que nos gustan.