/ nodejs

NodeJS en clases

Bien, hay muchas formas de hacer NodeJS, y ciertamente el framework que utilices para construir tu backend (por ejemplo, express), influye mucho en el cómo se diseña el código

Lo que quiero demostrar aquí es la posibilidad de escribir código basado en clases en una solución con NodeJS y Express

Instalar y configurar babel

Lo primero que necesitamos es babel para tener la habilidad del soporte de clases:

npm install --save-dev babel-cli babel-preset-es2015

La razón por la cuál la guardaremos como dependencia de desarrollo es porque babel no recomienda utilizar el transpilado en producción porque es innecesariamente lento, pero llegaremos a eso más tarde

Ahora debemos configurar babel para interpretar nuestro código, para esto, crea un archivo .babelrc y agrega el siguiente contenido:

{
  "presets": ["es2015"]
}

Configurar tareas / scripts de node

El siguiente paso será la tarea de node, en tu package.json, agrega / edita la tarea:

  "scripts": {
    "local": "babel-node YOUR_START_FILE.js"
  }

Ejemplo

Antes de pasar a un ejemplo, es necesario identificar qué componentes de tu aplicación requieren ser clases, por ejemplo, un router de express, no requiere ser una clase, ya estos se escriben de forma distinta:

router.get('YOUR_URL', (req, res) => {
  // este es el handler :o
})

Así que el diseño de express no necesita (no estoy diciendo que no se pueda) de un diseño basado en clases, para esto, utilizaremos una capa de servicios:

Crea un archivo services/authservice.js

import jwt from 'jwt-simple'

class AuthService {

  constructor (cualquierParametro) {
    // this sets locales
  }
  
  encodeToken (payload) {
    // el código específico de tu implementación iría aquí
  }
  
  decodeToken (token) {
    // código de tu implementación..
  }

}

export let authService = new AuthService()

Dos cosas a notar aquí:

  1. Estamos creando un constructor con argumentos y estamos instanciando SIN argumentos, la razón de esto es porque manejaremos la inyección de dependencias manualmente desde otro contenedor, como recomendación personal, es mejor dejar el constructor con argumentos para saber los atributos que serán utilizados en la clase
  2. Exportamos una instancia y no la clase. Al final, es JavaScript, no tenemos una forma de hacer interfaces para exportar componentes y hacer el plug a la interface / nickname / global component que deseemos (como lo hace spring con Java, por ejemplo!). Si deseas algo así, te recomiendo leer de typescript y sus integraciones con NodeJS. Retomando el punto, al exportar la instancia, nos facilita la inyección de dependencias de forma manual, ya que no tendríamos que lidear con crear las instancias de nuestro lado (pequeño truco de personas flojas como yo :) )

¿Cómo resolvemos las dependencias? Bien, usualmente lo que yo hago es utilizar express-load para cargar en orden la configuración global de la aplicación, plug de base de datos, solución MANUAL de dependencias, hook de rutas, por lo que el código iría en algo similar a lo siguiente:

app/serviceloader.js

import { authService } from '../services/authservice'

module.exports = app => {
  authService.cualquierParametro = yourDependency
  // por ejemplo: authService.tokenExpiry = app.get('token-expiry')
  // ó: authService.userService = userService
}

En mi github pueden encontrar un boiler plate muy básico que utiliza estos conceptos de inyección de dependencias y node usando EcmaScript 2015

¿Qué debo hacer para producción?

En realidad ya contamos con todo lo necesario para trabajarlo en producción, con algunos ajustes en las tareas, estaremos listos para que nuestra herramienta de CI pueda enviar correctamente el bundle que necesitamos:

package.json

  "scripts": {
    "build:prod": "babel YOUR_INDEX.js -d dist",
    "serve": "node dist/index.js"
  }

Ahora, nuestra tarea para liberación a PROD, debería hacer lo siguiente:

npm run build:prod # <<== genera el bundle

Ahora nuestro demonio / servicio debe considerar ejecutar node utilizando la tarea serve:

npm run serve

Esto correrá el output del bundle y tendremos una ejecución óptima (o al menos mejor comparada a usar babel-node directamente en producción