Refactorizando una aplicación Express
Ya sabemos que Express.js tiene una CLI que genera un proyecto, pero ahora vamos a convertir, en la medida de mis limitadas capacidades, en la sintaxis de ECMAScript 2016. Veamos si el resultado es exitoso.
Instalación de la CLI de Express
Primero, vamos a instalar el generador, instalando express
como paquete global.
yarn global add express
Con esto, estamos en posibilidad de usar la CLI. Estas son las opciones de generación.
$ express -h
Usage: express [options] [dir]
Options:
-h, --help output usage information
--version output the version number
-e, --ejs add ejs engine support
--pug add pug engine support
--hbs add handlebars engine support
-H, --hogan add hogan.js engine support
-v, --view <engine> add view <engine> support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)
-c, --css <engine> add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
--git add .gitignore
-f, --force force on non-empty directory
Generación del proyecto
Bien, vamos usar como motor de plantillas Handlebars, con la opción --view hbs
, el preprocesador de estilos será Sass, --css saas
y queremos que genere un archivo .gitignore
. Así que esta es la línea que usaremos,
$ express --view hbs --css sass --git nspaces
Y este es el resultado:
create : nspaces
create : nspaces/package.json
create : nspaces/app.js
create : nspaces/.gitignore
create : nspaces/public
create : nspaces/routes
create : nspaces/routes/index.js
create : nspaces/routes/users.js
create : nspaces/views
create : nspaces/views/index.hbs
create : nspaces/views/layout.hbs
create : nspaces/views/error.hbs
create : nspaces/bin
create : nspaces/bin/www
create : nspaces/public/javascripts
create : nspaces/public/images
create : nspaces/public/stylesheets
create : nspaces/public/stylesheets/style.sass
install dependencies:
$ cd nspaces && npm install
run the app:
$ DEBUG=nspaces:* npm start
Como podemos ver, creo cuatro directorios:
bin
— que contiene solo un archivo, llamadowww
y es el que ejecuta el proyecto.public
— contiene los archivos de plantillas, los scripts del front-end y los estilos.routes
— Contiene dos archivos, uno llamadoíndex.js
y otro llamadousers.js
.views
— Contiene plantillas de Handlebars, la base, una para errores y el índice.
Hay también un archivo app.js
, uno .gitignore
y un package.json
.
Archivo .gitignore
Ignora los archivos comunes del desarrollo con Node.js. No hay ninguna sorpresa, así que lo dejamos como esta.
Archivo packages.json
Este es el contenido:
{
"name": "nspaces",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"body-parser": "~1.17.1",
"cookie-parser": "~1.4.3",
"debug": "~2.6.3",
"express": "~4.15.2",
"hbs": "~4.0.1",
"morgan": "~1.8.1",
"node-sass-middleware": "0.9.8",
"serve-favicon": "~2.4.2"
}
}
La principal modificación que vamos a hacer es que vamos a agregar las dependencias de Babel y modificaremos el script de arranque.
yarn add --dev babel-cli babel-preset-es2015
Esto agrega una nueva sección a nuestro archivo, que junto con la configuración de Babel, se ve así:
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-preset-es2015": "^6.24.1"
},
"babel": {
"presets": ["es2015"]
}
Vamos a cambiar el comando del script start
, de node
a babel-node
y estamos listos para instalar el resto de las dependencias y probar si funciona Babel tal como está el proyecto actualmente.
$ yarn start
yarn start v0.24.5
$ babel-node ./bin/www
GET / 200 186.270 ms - 204
No hay nada que indique que el servidor ya arrancó o el puerto en el que está funcionando, pero si visitamos la página http://localhost:3000
podemos ver el resultado esperado. Ya podemos empezar a refactorizar.
Refactorizabdo app.js
Empezaremos con este archivo, cambiando las líneas como esta
var express = require('express');
a esto
import express from 'express'
Nuestros import
se ven así:
import express from 'express'
import path from 'path'
import favicon from 'serve-favicon'
import logger from 'morgan'
import cookieParser from 'cookie-parser'
import bodyParser from 'body-parser'
import sassMiddleware from 'node-sass-middleware'
y no hemos roto nada. Pero ahora siguen las rutas, donde se importan archivos locales, veamos si podemos usar la misma estructura.
Los cambiamos por esto:
import index from './routes/index'
import users from './routes/users'
y todo sigue funcionando.
La línea
var app = express();
cambia a
let app = express()
La configuración del motor de plantillas no requiere nada (solo quité el punto y coma) y se ve igual
// configuracion del motor de plantillas
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'hbs')
La configuración del favicon, la bitácora, el analizador de contenido bodyParser
y de cookies, cookieParser
tampoco cambia.
Cambiando a SCSS
Aquí damos un pequeño brinco, porque pasamos de Sass a SCSS. El cambio es solo cuestión de gustos. La plantilla cambia a esto:
body {
padding: 50px;
font: 14px "Noto Sans", Helvetica, Arial, sans-serif;
}
a {
color: indianred;
}
Dado el cambio en el procesador, de regreso en app.js
hacemos los ajustes necesarios.
app.use(sassMiddleware({
src: path.join(__dirname, 'public'),
dest: path.join(__dirname, 'public'),
indentedSyntax: false, // true = .sass and false = .scss
sourceMap: true
}));
Las rutas no cambian…
app.use('/', index)
app.use('/users', users)
Ni el generador de errores 404…
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found')
err.status = 404
next(err)
})
El siguiente manejado de errores, tampoco lo tocamos…
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message
res.locals.error = req.app.get('env') === 'development' ? err : {}
// render the error page
res.status(err.status || 500)
res.render('error')
})
Por último, exportamos la app. Sin cambios.
module.exports = app
Refactorizando bin
, primera parte
No me atrevo con este archivo, pero haremos el primer intento.
Primero cambiamos la línea que importa app
, para dejarla así:
import app from '../app' // var app = require('../app');
La siguiente línea es esta:
var debug = require('debug')('nspaces:server');
Y la cambiaré en dos líneas, creo…
import Debug from 'debug'
let debug = Debug('nspaces:server')
Ufff, que suerte. Funcionó.
Hay una función llamada normalizarPuerto
, que al cambiarla a let
debe colocarse antes de su primer uso, quedando así:
/**
* Normalizamos un puerto a número, cadena o false
*/
let normalizarPuerto = (val) => {
let port = parseInt(val, 10)
if (isNaN(port)) return val
if (port >= 0) return port
return false
}
/**
* El puerto lo obtenemos del entorno...
*/
let port = normalizarPuerto(process.env.PORT || '3000')
app.set('port', port)
Y lo mismo va a pasar con la función siHayError
…
/**
* Event listener for HTTP server "error" event.
*/
let siHayError = (error) => {
if (error.syscall !== 'listen') throw error
let bind = typeof port == 'string' ? `Pipe ${port}` : `Puerto ${port}`
// manejamos los errores con mensajes amigables
switch (error.code) {
case 'EACCES':
console.error(`${bind} requiere de mayores privilefios`)
process.exit(1)
break
case 'EADDRINUSE':
console.error(`El ${bind} ya está en uso`)
process.exit(1)
break;
default:
throw error
}
}
… y con alEscuchar
…
/**
* alEscuchar es un evento de "escucha" del servidor HTTP.
*/
let alEscuchar = () => {
let direccion = server.address()
let bind = typeof direccion === 'string' ? `el pipe ${direccion}` : `el puerto ${direccion.puerto}`
debug(`Escuchando en ${bind}`)
}
Al final el archivo se ve de esta manera:
/**
* Module dependencies.
*/
import app from '../app'
import http from 'http'
import Debug from 'debug'
let debug = Debug('nspaces:server')
/**
* Normalizamos un puerto a número, cadena o false
*/
let normalizarPuerto = (val) => {
let puerto = parseInt(val, 10)
if (isNaN(puerto)) return val
if (puerto >= 0) return puerto
return false
}
/**
* Event listener for HTTP server "error" event.
*/
let siHayError = (error) => {
if (error.syscall !== 'listen') throw error
let bind = typeof port == 'string' ? `Pipe ${port}` : `Puerto ${port}`
// manejamos los errores con mensajes amigables
switch (error.code) {
case 'EACCES':
console.error(`${bind} requiere de mayores privilefios`)
process.exit(1)
break
case 'EADDRINUSE':
console.error(`El ${bind} ya está en uso`)
process.exit(1)
break;
default:
throw error
}
}
/**
* alEscuchar es un evento de "escucha" del servidor HTTP.
*/
let alEscuchar = () => {
let direccion = server.address()
let bind = typeof direccion === 'string' ? `el pipe ${direccion}` : `el puerto ${direccion.port}`
debug(`Escuchando en ${bind}`)
}
/**
* El puerto lo obtenemos del entorno...
*/
const puerto = normalizarPuerto(process.env.PORT || '3000')
app.set('port', puerto)
/**
* Creamos el servidor HTTP...
*/
const server = http.createServer(app);
/**
* Escuchamos el puerto determinado y todas las interfaces de red...
*/
server.listen(puerto)
server.on('error', siHayError)
server.on('listening', alEscuchar)
Refactorizando las rutas
Estos archivos son pequeños, así que es fácil refactorizarlos.
Este el archivo routes/index.js
refactorizado:
import express from 'express'
let _rutaIndex = express.Router()
/* GET de la portada */
_rutaIndex.get('/', (req, res, next) => res.render('index', { title: 'nSpaces' }))
module.exports = _rutaIndex
Y este es el archivo routes/users.js
import express from 'express'
let _rutaUsers = express.Router()
/* De usuarios, creo. */
_rutaUsers.get('/', (req, res, next) => res.send(`respondiendo con un recurso`))
module.exports = _rutaUsers
Refactorizando las plantillas
Realmente la única que requiere cambios es index.hbs
y solo implica cambiar el Welcome to
a Bienvenido a
…
<h1>{{title}}</h1>
<p>Bienvenido a {{title}}</p>
Liberando la versión refactorizada
Como ya está listo el proyecto con la sintaxis de ES6, vamos a liberar una versión para que podamos usarla en futuros desarrollos.
Esta es la página del proyecto nSpaces en GitHub versión v0.1.0 firmada con GnuPG para mayor seguridad.