Cómo ejecutar varios scripts npm en paralelo

Nota: puedes encontrar todos los archivos en este gist.

Quiero empezar este nuevo blog con un artículo corto, pero que, particularmente encuentro muy útil.

A menudo cuando estás desarrollando te encuentras con varios terminales abiertos porque necesitas tener múltiples herramientas o aplicaciones abiertas para ir testeando tu aplicación. Un escenario muy típico cuando desarrollas aplicaciones web, por ejemplo, es tener un servidor web local para tus páginas y, al mismo tiempo, una base de datos para que tu aplicación interactúe con ella.

Si preferirías lanzar con un solo comando el servidor web y la base de datos continúa leyendo y te mostraré cómo lo hago yo.

Voy a mostrarte un ejemplo concreto donde voy a utilizar JSON server para actuar como API backend y Browser Sync para servir una serie de páginas estáticas. Esta suele ser una configuración básica que utilizo para trabajar en un proyecto de frontend, pero puede extrapolarse a cualquier otro escenario en el que necesites ejecutar múltiples comandos en paralelo con un solo script de npm.

Preparación del proyecto

En primer lugar voy a preparar un simple proyecto de ejemplo.

$ mkdir npm-paralelo
$ cd npm-paralelo
$ npm init -y

Wrote to \projects\npm-paralelo\package.json:

{
  "name": "npm-paralelo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "Tony G. Bolaño (https://tonygb.com)",
  "license": "MIT"
}

He usado la opción -y para usar los datos por defecto en la configuración inicial del archivo package.json.

Una vez inicializado el proyecto, instalamos nuestras dos dependencias básicas, JSON Server y Browser Sync.

$ npm install --save-dev browser-sync json-server

Browser Sync

Creamos un directorio html que servirá de carpeta raíz para el servidor Browser Sync.

$ mkdir html

Desde la línea de comandos ejecutamos Browser Sync y le decimos que funcione en modo servidor y nos sirva todo el contenido del directorio html que hemos creado:

$ ./node_modules/.bin/browser-sync start --server --serveStatic html

Browser Sync se inicia y a su vez nos abre un navegador que apunta a http://localhost:3000 que es la configuración por defecto. Al mirar el navagador nos aparece el mensaje:

Cannot GET /

Esto es porque todavía no hemos creado ninguna página HTML y, por tanto, nuestra web local está vacía. Arreglemos esto creando un archivo HTML muy simple que vamos a guardar en la carpeta html con el nombre index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>NPM: scripts en paralelo</title>
</head>
<body>
    <h1>Probando ejecución en paralelo con NPM</h1>
</body>
</html>

Al recargar la página en el navegador, pordremos ver el título «Probando ejecución en paralelo con NPM»

JSON Server

JSON Server proporciona un API RESTful bastante amplio a partir de un archivo JSON, lo que resulta muy útil para desarrollar frontend.

Lo primero que necesitamos es un fichero JSON con algunos datos que podamos consultar a través de JSON Server. Voy a utilizar directamente el archivo JSON que aparece en el archivo README del propio JSON Server.

En el directorio raíz del proyecto creamos el archivo db.json e insertamos el siguiente contenido:

{
  "posts": [
    { "id": 1, "title": "json-server", "author": "typicode" }
  ],
  "comments": [
    { "id": 1, "body": "some comment", "postId": 1 }
  ],
  "profile": { "name": "typicode" }
}

Para probar el funcionamiento de JSON Server abrimos otro terminal (ya que en el primero tenemos lanzado Browser Sync) y, después de movernos hasta el directorio de nuestro proyecto, ejecutamos el siguiente comando:

$ json-server --watch db.json --port 3003

  \{^_^}/ hi!

  Loading db.json
  Done

  Resources
  http://localhost:3003/posts
  http://localhost:3003/comments
  http://localhost:3003/profile

  Home
  http://localhost:3003

  Type s + enter at any time to create a snapshot of the database
  Watching...

He especificado un puerto para evitar coincidir en el puerto 3000 usado por Browser Sync. Si ahora navegamos hasta la URL http://localhost:3003/posts/1, JSON Server nos devuelve un JSON con los datos del post con ID igual a uno.

{
  "id": 1,
  "title": "json-server",
  "author": "typicode"
}

Crear scripts de npm

Puesto que cuando estemos desarrollando nuestro proyecto tendremos que lanzar múltiples veces Browser Sync y JSON Server, decidimos crearnos un script de npm para cada uno, de forma que no tengamos que recordar todos los parámetros del comando cada vez.

Antes de continuar detenemos Browser Sync y JSON Server mediante Ctrl + C en ambos terminales.

Lanzar Browser Sync con npm

Editamos el archivo package.json y, en el apartado scripts agregamos la siguiente línea:

...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "browser-sync": "browser-sync start --server --serveStatic html"
  },
...

Probamos que todo funciona como antes ejecutando nuestro nuevo script de npm:

$ npm run browser-sync

> npm-paralelo@1.0.0 browser-sync /projects/npm-paralelo
> browser-sync start --server --serveStatic html

[Browsersync] Access URLs:
 --------------------------------------
       Local: http://localhost:3000
    External: http://172.17.13.222:3000
 --------------------------------------
          UI: http://localhost:3001
 UI External: http://172.17.13.222:3001
 --------------------------------------
[Browsersync] Serving files from: ./

Como vemos, todo continua funcionando igual, pero ahora no necesitamos recordar todas las opciones de Browser Sync, simplemente usamos npm run browser-sync para lanzarlo.

Lanzar JSON Server

Utilizamos la misma técnica con JSON Server. Modificamos la sección scripts de nuestro package.json que quedará así:

...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "browser-sync": "browser-sync start --server --serveStatic html",
    "json-server": "json-server --watch db.json --port 3003"   
  },
...

De nuevo comprobamos que funciona ejecutando en el segundo terminal la siguiente instrucción.

$ npm run json-server

> npm-paralelo@1.0.0 json-server /projects/npm-paralelo
> json-server --watch db.json --port 3003


  \{^_^}/ hi!

  Loading db.json
  Done

  Resources
  http://localhost:3003/posts
  http://localhost:3003/comments
  http://localhost:3003/profile

  Home
  http://localhost:3003

  Type s + enter at any time to create a snapshot of the database
  Watching...

Uso de concurrently

Con este método, necesitamos mantener abiertos dos terminales mientras desarrollamos: uno para bowser sync y otro para JSON Server. Podrían ser más, en función de la configuración y las necesidades de nuestro proyecto (por ejemplo RabbitQM y Reddis para las colas de mensajes).

Para evitar mantener tantos terminales abiertos podemos usar concurrently.

Instalar concurrently

La instalación, como es habitual, la realizaremos usando npm:

$ npm install --save-dev concurrently

Una vez instalado, detenemos de nuevo Browser Sync y JSON Server utilizando Ctrl + C para evitar colisiones al usar nuestro nuevo script.

Crear script de npm

De nuevo modificamos la sección de scripts del archivo package.json que queda de la siguiente manera:

...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "browser-sync": "browser-sync start --server --serveStatic html",
    "json-server": "json-server --watch db.json --port 3003",
    "dev": "concurrently \"npm run browser-sync\" \"npm run json-server\""
  },
...

Ahora podemos ejecutar los dos comandos en paralelo con una sola orden:

$ npm run dev

> npm-paralelo@1.0.0 dev /projects/npm-paralelo
> concurrently "npm run browser-sync" "npm run json-server"

[1]
[1] > npm-paralelo@1.0.0 json-server /projects/npm-paralelo
[1] > json-server --watch db.json --port 3003
[1]
[0]
[0] > npm-paralelo@1.0.0 browser-sync /projects/npm-paralelo
[0] > browser-sync start --server --serveStatic html
[0]
[1]
[1]   \{^_^}/ hi!
[1]
[1]   Loading db.json
[1]   Done
[1]
[1]   Resources
[1]   http://localhost:3003/posts
[1]   http://localhost:3003/comments
[1]   http://localhost:3003/profile
[1]
[1]   Home
[1]   http://localhost:3003
[1]
[1]   Type s + enter at any time to create a snapshot of the database
[1]   Watching...
[1]
[0] [Browsersync] Access URLs:
[0]  --------------------------------------
[0]        Local: http://localhost:3000
[0]     External: http://172.17.13.222:3000
[0]  --------------------------------------
[0]           UI: http://localhost:3001
[0]  UI External: http://172.17.13.222:3001
[0]  --------------------------------------
[0] [Browsersync] Serving files from: ./

Como puedes observar, concurrently numera cada uno de los procesos lanzados en paralelo, de forma que resulta fácil identificar qué líneas en la consola corresponden a la salidad de un proceso o del otro. En esta ejecución concreta, por ejemplo, las líneas que comienzan por [0] corresponden a Browser Sync y las que comienzan por [1] corresponden a JSON Server.

Si quieres leer más acerca de concurrently visita el código fuente del proyecto en GitHub.

Conclusión

Hemos visto como es posible ejecutar dos tareas de npm en paralelo desde un mismo terminal, lo que nos puede resultar cómodo a la hora de desarrollar una aplicación en local que necesite de varias herramientas corriendo a la vez. En este ejemplo hemos utiliado, por simplicidad, Browser Sync y JSON Server, pero podría servir para multitud de procesos que usamos habitualmente en nuestros desarrollo como podría ser Angular o una base de datos como MongoDb.

Espero que te haya resultado útil.

Te espero en el próximo artículo.

Stay tuned and happy coding!

Escrito el 2 de octubre de 2017 |