Cómo escribir una aplicación Node and Express lista para producción

Estructuración de proyectos

Cuando comencé a crear aplicaciones Node & Express, no sabía lo importante que era estructurar su aplicación. Express no viene con reglas o pautas estrictas para mantener la estructura del proyecto.

Eres libre de usar cualquier estructura que quieras. Cuando su código base crece, termina teniendo routemanejadores largos . Esto hace que su código sea difícil de entender y contiene errores potenciales.

Si está trabajando para una startup, la mayoría de las veces no tendrá tiempo para refractar su proyecto o modularizarlo. Puede terminar con un ciclo interminable de corrección de errores y parches.

Con el tiempo, mientras trabajaba con equipos pequeños y grandes, me di cuenta de qué tipo de estructura puede crecer con su proyecto y seguir siendo fácil de mantener.

Controlador de vista de modelo

El patrón MVC ayuda en un desarrollo rápido y paralelo. Por ejemplo, un desarrollador puede trabajar en la vista, mientras que otro puede trabajar en la creación de la lógica empresarial en el controlador.

Echemos un vistazo a un ejemplo de una aplicación CRUD de usuario simple.

project/ controllers/ users.js util/ plugin.js middlewares/ auth.js models/ user.js routes/ user.js router.js public/ js/ css/ img/ views/ users/ index.jade tests/ users/ create-user-test.js update-user-test.js get-user-test.js .gitignore app.js package.json
  • controladores: defina los controladores de ruta de su aplicación y la lógica empresarial
  • util: escribe aquí funciones de utilidad / ayuda que pueden ser utilizadas por cualquier controlador. Por ejemplo, puede escribir una función como mergeTwoArrays(arr1, arr2).
  • middlewares: puede escribir middlewares para interpretar todas las solicitudes entrantes antes de pasar al controlador de ruta. Por ejemplo,

    router.post('/login', auth, controller.login)donde authes una función de middleware definida en middlewares/auth.js.

  • modelos: también una especie de middleware entre su controlador y la base de datos. Puede definir un esquema y realizar alguna validación antes de escribir en la base de datos. Por ejemplo, puede usar un ORM como Mongoose, que viene con excelentes funciones y métodos para usar en el esquema mismo.
  • rutas: defina las rutas de su aplicación, con métodos HTTP. Por ejemplo, puede definir todo lo relacionado con el usuario.
router.post('/users/create', controller.create) router.put('/users/:userId', controller.update) router.get('/users', controller.getAll)
  • público: almacena imágenes estáticas en /imgarchivos JavaScript personalizados y CSS/css
  • vistas: Contiene plantillas para ser renderizadas por el servidor.
  • tests: Aquí puede escribir todas las pruebas unitarias o pruebas de aceptación para el servidor API.
  • app.js: actúa como el archivo principal del proyecto donde inicializa la aplicación y otros elementos del proyecto.
  • package.json: se encarga de las dependencias, los scripts para ejecutar con el npmcomando y la versión de su proyecto.

Excepciones y manejo de errores

Este es uno de los aspectos más importantes a tener en cuenta a la hora de crear cualquier proyecto con cualquier idioma. Veamos cómo manejar errores y excepciones con elegancia en una aplicación Express.

Usando promesas

Una de las ventajas de usar promesas sobre devoluciones de llamada es que pueden manejar excepciones / errores implícitos o explícitos en bloques de código asíncrono, así como para el código síncrono definido en .then()una devolución de llamada de promesa.

Solo agregue .catch(next)al final de la cadena de promesas. Por ejemplo:

router.post('/create', (req, res, next) => { User.create(req.body) // function to store user data in db .then(result => { // do something with result return result }) .then(user => res.json(user)) .catch(next) })

Usando try-catch

Try-catch es una forma tradicional de detectar excepciones en código asincrónico.

Echemos un vistazo a un ejemplo con la posibilidad de obtener una excepción:

router.get('/search', (req, res) => { setImmediate(() => { const jsonStr = req.query.params try { const jsonObj = JSON.parse(jsonStr) res.send('Success') } catch (e) { res.status(400).send('Invalid JSON string') } }) })

Evite el uso de código síncrono

Código síncrono también conocido como código de bloqueo, porque bloquea la ejecución hasta que se ejecutan.

Por lo tanto, evite el uso de funciones o métodos síncronos que pueden tardar milisegundos o microsegundos. Para un sitio web de alto tráfico, se agravará y puede conducir a una alta latencia o tiempo de respuesta de las solicitudes de la API.

No los use en producción especialmente :)

Muchos módulos de Node.js vienen con métodos .syncy .async, así que use async en producción.

Pero, si aún desea usar una API síncrona, use la --trace-sync-iomarca de línea de comandos. Imprimirá una advertencia y un seguimiento de la pila cada vez que su aplicación use una API síncrona.

Para obtener más información sobre los fundamentos del manejo de errores, consulte:

  • Manejo de errores en Node.js
  • Creación de aplicaciones de nodo robustas: manejo de errores (blog StrongLoop)
Lo que no debe hacer es escuchar el uncaughtExceptionevento, emitido cuando una excepción burbujea hasta el ciclo del evento. Generalmente no se prefiere su uso.

Registrando correctamente

El registro es esencial para la depuración y la actividad de la aplicación. Se utiliza principalmente con fines de desarrollo. Usamos console.logy, console.errorpero estas son funciones sincrónicas.

Para fines de depuración

Puedes usar un módulo como debug. Este módulo le permite usar la variable de entorno DEBUG para controlar a qué mensajes de depuración se envían console.err(), si corresponde.

Para la actividad de la aplicación

Una forma es escribirlos en la base de datos.

Consulte Cómo utilicé los complementos de mangosta para realizar auditorías de mi aplicación.

Another way is to write to a file OR use a logging library like Winston or Bunyan. For a detailed comparison of these two libraries, see the StrongLoop blog post Comparing Winston and Bunyan Node.js Logging.

require(“./../../../../../../”) mess

There are different workarounds for this problem.

If you find any module getting popular and if it has logical independence from the application, you can convert it to private npm module and use it like any other module in package.json.

OR

const path = require('path'); const HOMEDIR = path.join(__dirname,'..','..');

where __dirname is the built-in variable that names the directory that contains the current file, and .. ,..is the requisite number of steps up the directory tree to reach the root of the project.

From there it is simply:

const foo = require(path.join(HOMEDIR,'lib','foo')); const bar = require(path.join(HOMEDIR,'lib','foo','bar'));

to load an arbitrary file within the project.

Let me know in the comment below if you have better ideas :)

Set NODE_ENV to “production”

The NODE_ENV environment variable specifies the environment in which an application is running (usually, development or production). One of the simplest things you can do to improve performance is to set NODE_ENVto “production.”

Setting NODE_ENV to “production” makes Express:

  • Cache view templates.
  • Cache CSS files generated from CSS extensions.
  • Generate less verbose error messages.

Tests indicate that just doing this can improve app performance by a factor of three!

Using Process Manager

For production, you should not simply use node app.j — if your app crashes, it will be offline until you restart it.

The most popular process managers for Node are:

  • StrongLoop Process Manager
  • PM2
  • Forever

I personally use PM2.

For a feature-by-feature comparison of the three process managers, see //strong-pm.io/compare/. For a more detailed introduction to all three, see Process managers for Express apps.

Run your app in a cluster

In a multi-core system, you can increase the performance of a Node app by many times by launching a cluster of processes.

A cluster runs multiple instances of the app, ideally one instance on each CPU core. This distributes the load and tasks among the instances.

Using Node’s cluster module

Clustering is made possible with Node’s cluster module. This enables a master process to spawn worker processes. It distributes incoming connections among the workers.

However, rather than using this module directly, it’s far better to use one of the many tools out there that do it for you automatically. For example node-pm or cluster-service.

Using PM2

For pm2 you can use cluster directly through a command. For example,

# Start 4 worker processes pm2 start app.js -i 4 # Auto-detect number of available CPUs and start that many worker processes pm2 start app.js -i max 

If you encounter any problems, feel free to get in touch or comment below.

I would be happy to help :)

Don’t hesitate to clap if you considered this a worthwhile read!

References: //expressjs.com/en/advanced/best-practice-performance.html

Originally published at 101node.io on September 30, 2018.