Servidor web mediante Express
Descripción
- Montar un servidor web mediante paquetes propios de node
Objetivos
- Entender la arquitectura de Express
- Middleware
- Uso de paquetes para extender la funcionalidad de Express
- Creación de un servidor web que sirva:
- Contenido estático
- Contenido dinámico en base a templates
- Content-type en JSON (para API posterior)
Empezamos proyecto
Documentación
- https://expressjs.com/
- Extensa pero concisa
- Numerosos ejemplos
express
Instalar express mediante uno de los comandos siguientes:
npm install --save [email protected] npm i -S [email protected]
Creamos el fichero server.js con el siguiente código:
const express = require('express')
const app = express()
app.get('/', (req, res) => {
res.send('Hola Mundo')
})
app.listen(3000)
Ejecución
- Debería ejecutarse de cualquiera de las siguientes maneras:
npm start
node server.js
- Utiliza nodemon para evitar reinicios al cambiar código
Prueba funcionamiento
- Se echa de menos algún tipo de mensaje de arranque
- Mira en la documentación como añadir esa opción
- Cuando el método listen haya terminado...
Método listen
- Permite añadir más parámetros
- Un parámetro de tipo función (función de callback)
- Es el único parámetro de este tipo, así los diferencia
app.listen([port[, host[, backlog]]][, callback])
app.listen(3000, () => {
console.log('Servidor web arrancado en el puerto 3000')
})
Añadir otra ruta
- Utiliza el plugin ExpressSnippet de Visual Code para completado
- Comprueba su funcionamiento con el app.listen
- Datos nueva ruta:
- URL: /contactar
- Muestre el mensaje Página para contactar
Código ruta contactar
app.get('/contactar', (req, res) => {
res.send('Página para contactar')
})
Uso de JSON
- Vamos a devolver un JSON en vez de un string:
app.get('/contactar', (req, res) => {
res.send({
nombre: 'pepito'
email: '[email protected]'
})
})
Comprobar JSON
Utiliza algún plugin de formateo de JSON dentro del navegador
El JSON puede ser más complejo que el anterior, por ej:
https://api.arasaac.org/api/pictograms/es/search/casa
Comprueba como cambia el content type
- Utiliza las herramientas de desarrollo
- El cambio lo hace directamente Express
- Los datos en JSON podrían estar:
- En una variable
- Incluso en otro fichero
- Recibirse vía API
- ....
const contacto = require('./contacto.json')
app.get('/contactar', (req, res) => {
res.send(contacto)
})
Middleware
- Son funciones que:
- Se registran por express mediante app.use y se ejecutan en orden
- Tienen acceso al objeto de solicitud (req), al objeto de respuesta (res) y a la siguiente función de middleware (next)
app.use((req, res, next)=>{
//operaciones del middleware
next() //para ir al siguiente middleware o a la ruta
// también podríamos hacer un send() y cortar
// la cola de middlewares, por ej en un control de permisos
})
Ejemplo de middleware
- Podemos crear un middleware que guarde traza de las fechas de accesos
- Para ver las propiedades que nos hacen falta podemos usar la API de Express
- Es importante el orden
- Entre los middlewares
- Antes que las peticiones get, post....
var app = express()
app.use(function (req, res, next) {
var now = new Date().toString()
console.log(`Time: ${now} ${req.method} ${req.url}`)
next()
})
Logs a fichero
- Completa el middleware anterior para que guarde los cambios en el fichero server.log
- Piensa donde se debe poner el next() y si debes utilizar un método síncrono o asíncrono
Solución logs a fichero
app.use((req, res, next) => {
var now = new Date().toString()
var log = `${now}: ${req.method} ${req.url}`
console.log(log)
fs.appendFile('server.log', `${log}\n`, (err) => {
if (err) console.log(`No se ha podido usar el fichero de log: ${err}`)
})
next()
})
Más sobre logs
- Podemos querer utilizar distintos transports o medios para logs
- Ficheros
- Consola
- Distintos niveles (debug, err, warning...)
- Distintos formatos de visualización (colores, negrita...)
- Con posibilidad de ejecución de queries
- ....
- Lo mejor es usar algún paquete que ya exista
Uso de contenido estático
Crea un fichero .html en la carpeta public
- Ayúdate de emmet: ! + tab
Express ya tiene un middleware integrado para contenido estático
- No deja de ser una función como las vistas anteriormente
- No necesitamos importarla mediante un require
- Una vez importado, hace un send() si existe el fichero, si no, un next()
Configuración express para contenido estático
const staticRoute = path.join(__dirname, 'public')
app.use(express.static(staticRoute))
- __dirname es la raíz del proyecto
path.join para que sea multiplataforma
Podríamos configurar un directorio virtual (static para public)
const staticRoute = path.join(__dirname, 'public')
app.use('/static', express.static(staticRoute))
Template engine
Mostrar un index.html está bien pero puede que necesitemos:
- Inyectar valores en el html
- "includes" para footer, header... (patrón diseño DRY)
Tenemos que elegir un motor de plantillas
- hbs, ejs, pug (jade)...
- Utilizaremos hbs que es un wrapper de handlebars
Configuración de hbs
Instalación
npm i -S hbs
Configuración
// const hbs = require('hbs') app.set('view engine', 'hbs'); // clave valor
- No hacemos un require porque no usamos ninguna función
- express lo llama internamente
- Indicamos a nodemon los tipos de ficheros a monitorizar (por defecto solo js):
"scripts": {
"start": "nodemon server.js -e js,hbs"
},
Uso de hbs
- Definimos una carpeta views donde irán las templates
- Fichero views/contactar.hbs:
...
<body>
<h1>{{pageTitle}}</h1>
<p>Aquí iría el formulario de contacto</p>
<footer>
<p>Copyright {{currentYear}}</p>
</footer>
</body>
....
- Ejecutamos el método res.render() en vez de res.send()
- Admite un objeto como segundo parámetro para pasar variables
app.get('/contactar', (req, res) => {
res.render('contactar.hbs', {
pageTitle: 'Contactar',
currentYear: new Date().getFullYear()
})
})
Partials mediante handlebars
- Registramos el directorio donde se van a guardar:
const hbs = require('hbs')
hbs.registerPartials(path.join(__dirname, 'views', 'partials'))
app.set('view engine', 'hbs') // clave valor
- Creamos fichero views/partials/footer.hbs:
<footer>
<p>Copyright {{getCurrentYear}}</p>
</footer>
- Lo incluimos dentro de nuestro fichero views/contactar.hbs:
...
<body>
<h1>{{pageTitle}}</h1>
<p>Aquí iría el formulario de contacto</p>
{{> footer}}
<!-- con la linea anterior
tendríamos el footer mediante partials -->
</body>
....
Ejercicio templates
- Añade una página de inicio además de contactar
- Ambas deben cargar su correspondiente template que además cargará un partial para el header y otro para el footer
Uso de helpers
- Se registran funciones que devuelven un código dinámico
- Se pueden inyectar en cualquier template o partial.
hbs.registerHelper('getCurrentYear', () => new Date().getFullYear())
// con paso de parámetro:
hbs.registerHelper('toUpperCase', text => text.toUpperCase())
- Podríamos eliminar el paso de currentYear a las vistas y utilizar el helper:
<footer>
<p>Copyright {{getCurrentYear}}</p>
<p>{{toUpperCase "Licencia MIT"}}</p>
{{>header}}
</footer>
¿Continuamos?
- Ya estamos preparados para utilizar express para crear una API