Mantener tu código limpio es muy importante, mantener un repositorio con diferentes estilos para codificar es un dolor de cabeza, pero algunas veces es difícil conseguir como organizar tu código para una aplicación en express, porque hay muchas maneras de hacerlo. Hoy te proporcionaré algunos tips para ayudarte a escribir un código más legible en tu aplicación.

La clave para mantener un código legible es la consistencia, es importante usar un “linter” para mantener darte cuenta de los errores o mal indentación de tu código, te recomiendo eslint, hay muchos complementos para integrarlo fácilmente en diferentes editores de texto, de esta forma puedes definir reglas para tu codigo, como por ejemplo, si deseas que la indentación sea de 2 o 4 espacios, si no estás familiarizados con los “linters” deberías usar la configuración estándar de eslint, para ello deberás crear un archivo llamado .eslintrc.json en la carpeta principal de tu proyecto, donde esta el package.json.

// .eslintrc.json example

{
  "env": {
    "node": true,
    "es6": true
  },
  "extends": "eslint:recommended"
}

La parte más importante de una aplicación de express son las funciones middleware y los rutas de las api rest que usan middleware en cadena, así que vamos a centrarnos en esto, si no sabes que es una función middleware te animo a que vayas a mi publicación anterior: “Entendiendo la función middleware de Express.js”.

Empecemos con algunos ejemplos de que deberías y que no deberías hacer cuando defines tus rutas de express. Deberías empezar con tus dependencias externas, seguido por tus dependencias internas como por ejemplo, tus modelos de base de datos o algunos middleware que hayas creado.

// user.routes.js

// Evitar! No esta tan mal, pero creeme, perderás algo de tiempo tratando de entender esto.

const express = require('express');
const auth = require(‘..middlewares/auth.middleware’);
const mongoose = require('mongoose');
const User = require('../models/user.model');
const router = express.Router();

// ... tus rutas aqui

// Bien! Puedes ver fácilmente cuáles son tus dependencias externas e internas.

const express = require('express');
const mongoose = require('mongoose');

const router = express.Router();
const auth = require(‘..middlewares/auth.middleware’);
const User = require('../models/user.model');

// ... tus rutas aqui

// Mal! Usa métodos en cadena, cuando estés definiendo varias llamadas con la misma ruta, en este caso la ruta es ‘/user’.

// … dependencias aqui

router.route('/user/').patch(auth.isSelf, findByIdAndUpdate)
router.route('/user/').delete(auth.isSelf, findByIdAndRemove)
router.route('/user/').get(find);

// … middlewares aqui

// Aun peor! Los métodos en cadena deben estar bien indentados para que sean fáciles de entender!

// … dependencias aqui

router.route('/user/').patch(auth.isSelf, findByIdAndUpdate).delete(auth.isSelf, findByIdAndRemove).get(findAll);

// … middlewares aqui

// Perfecto! Así es como deberías indentar tus metodos en cadena.

// … dependencias aqui

router
  .route('/user/')
  .patch(auth.isSelf, findByIdAndUpdate)
  .delete(auth.isSelf, findByIdAndRemove)
  .get(findAll);

// … middlewares aqui

// Finalmente!

const express = require('express');
const mongoose = require('mongoose');

const router = express.Router();
const auth = require(‘..middlewares/auth.middleware’);
const User = require('../models/user.model');

router
  .route('/user/')
  .patch(auth.isSelf, findByIdAndUpdate)
  .delete(auth.isSelf, findByIdAndRemove)
  .get(findAll);

function findByIdAndUpdate(req, res, next) {
   // tu logica aqui
}

function findByIdAndRemove(req, res, next) {
  // tu logica aqui
}

function findAll(req, res, next) {
  // tu logica aqui
}

module.exports = router;

Ten en cuenta que no usé funciones anónimas para los middleware, sino que definimos una función aparte y pasamos las funciones como parámetros, de esta forma se puede ver todas las rutas definidas al principio del archivo, si tu archivo empieza a ponerse largo puedes dividir las rutas en varios archivos usando la lógica, por ejemplo, si tienes unas rutas que no solo interactúan con el modelo User sino que usen otros modelos en tu base de datos, puedes poner esas rutas en otro archivo dentro de la carpeta user, nunca deberías tener rutas de distintos modelos en un mismo archivo, utiliza un archivo para cada uno para mantener el orden y la lógica.

Algunas veces puedes usar funciones anónimas, cuando la función sea corta o creas q no es necesario definir una función más para ese código, un buen ejemplo de esto es cuando se realiza un query con el framework mongoose para traer una lista de documentos, ésta toma una función callback con dos parámetros, para saber si hubo un algún error o para tomar la lista. Además, siempre uso funciones de flecha en lugar de funciones anónimas.

// No esta mal usar una función tipo flecha como callback en este caso (err, users) =>, ya que es una función corta y es fácil de entender.

function findAll(req, res, next) {
  User
    .find({})
    .exec((err, users) => {
      if (err) return next(err);

      if (!users) {
        return res.status(404).json({
          message: 'No users found',
        })
      }

      res.status(200).json(users);
    });
}

// También puedes definir una función si te resulta mejor.

function find(req, res, next) {
  User
    .find({})
    .exec(usersQuery);

  function usersQuery(err, users) {
    if (err) return next(err);

    if (!users) {
      return res.status(404).json({
        message: 'No users found',
      })
    }

    res.status(200).json(users);
  }
}

Quizá pienses que esta bien crear funciones anónimas en cadena, ¿No es tan malo cierto? Creeme, si lo es, si quieres reutilizar un middleware tienes que declararlo como una función aparte, por ejemplo, no querrás repetir tu codigo cada vez que quieras verificar el token de un usuario, además, las funciones en cadena no son buenas para tus ojos, veamos a lo que me refiero, es una función muy larga así que no trates de entenderla porque perderías mucho tiempo.

// Esta función es muy difícil de entender ¡No la leas!.

router.route('/user/').get(function(req, res, next) {
  const token = req.headers['x-access-token'];

  if (token) {
    jwt.verify(token, process.env.TOKEN_SECRET, decodeToken);
  } else {
    return res.status(403).json({
      message: 'No token provided.',
    });
  }

  function decodeToken(err, decoded) {
    if (err) {
      return res.status(403).json({
        message: 'Failed to authenticate token.',
        error: serializeError(err),
      });
    }

    req.user = decoded;
    next();
  }
}, function(req, res, next) {
  User
    .find({
      $nor: [{
        "_id": req.user._id
      }]
    })
.sort({
      firstname: 'asc',
    })
    .exec((err, users) => {
      if (err) return next(err);

      if (!users) {
        return res.status(404).json({
          message: 'No users found',
        })
      }

      res.status(200).json(users);
    });
});

Probablemente no te diste cuenta que solo habian dos funciones anónimas ahí, eso es lo que pasa cuando colocas funciones anónimas largas en cadena, incluso si las hiciste tú mismo, podrías revisarlas al dia siguiente y no entenderás que sucede en ese código.

Espero haberte ayudado a entender cómo escribir un código más legible para tu aplicación de express o cualquier aplicación hecha con javascript, y porqué deberías hacerlo, ya que es necesario mantener un código legible si deseas trabajar en equipo y que tu aplicación sea mantenible y escalable.