Mostrando entradas con la etiqueta Bower. Mostrar todas las entradas
Mostrando entradas con la etiqueta Bower. Mostrar todas las entradas

martes, 10 de diciembre de 2013

Deploy de una app hecha con Grunt y Bower, desde Cloud9, en Heroku

¿Qué es Heroku?

Heroku es una plataforma como servicio (PaaS) en la nube, que nos permite arrancar un entorno de desarrollo para el lenguaje que queramos, de un modo transparente, y sin tener que perder tiempo por nuestra parte en configurar nada en nuestro sistema local. Podríamos decir que es un entorno de desarrollo "llave en mano". Al crear una aplicación en Heroku, éste nos ofrece un entorno sobre Debian, preparado ya para programar con ruby, php, java, node, o el lenguaje que necesitemos.

Nos ofrece también un repositorio Git, donde alojar nuestros fuentes. Cada vez que hagamos un push, Heroku compilará nuestro proyecto, y un servidor web al que podremos acceder para ver nuestra aplicación online: http://nuestraapp.herokuapp.com/

Para este pequeño "manual", voy a suponer que ya estás familiarizado con el desarrollo de aplicaciones con Grunt, Less, Jade, etc. Por tanto, entiendo que tu aplicación ya tiene un archivo package.json. Lo que voy a explicar aquí, es cómo modificarla para poder hacer deploys en Heroku, desde Cloud9:

Instalar el motor Node

Para que Node funcione correctamente en Heroku, debemos especificar en package.json que vamos a usar Node.js junto con su versión: algo que no era necesario para compilar en local, pero sí lo es en Heroku. Para ello, debemos añadir la propiedad "engines" a nuestro package.json:

"engines": {
     "node": "0.10.x"
}

No es necesario especificar que necesitaremos npm, ni su versión, ya que npm viene en el lote del motor Node.

Instalar Express, Grunt y Bower en nuestra app de Heroku

Por defecto, cuando compilamos una aplicación en Heroku, ésta se compila en modo producción, por lo tanto, lo que pongamos en "devDependencies" de nuestro package.json será ignorado. Llegados a este punto podemos hacer dos cosas:

  • o cambiamos el entorno a modo desarrollo, de tal modo que se instalen las dependencias definidas en "devDependencies",
  • o especificamos en "dependencies" las dependencias para producción.

Personalmente prefiero esta última opción, es decir, usar el entorno de producción en Heroku. Por tanto, copiamos los paquetes de "devDependencies" a "dependencies", y además, le añadimos también algunos paquetes que por defecto en Heroku no están instalados: express, bower, grunt y grunt-cli. Nuestro package.json quedaría así (por ejemplo):

"dependencies": {
  "express": "~3.3.4",
  "bower": "latest",
  "grunt": "latest",
  "grunt-cli": "latest",
  ...
  resto de paquetes
  ...
},
"devDependencies": {
  "grunt": "latest",
  ...
  resto de paquetes
  ...
}

Ejecutar Bower install en Heroku

Si en nuestra app hemos usado Bower, necesitaremos ejecutar el comando 'bower install' para instalar los repositorios al compilar. Para ello, añadimos la instrucción al bloque "scripts" en package.json:

"scripts": {
    "postinstall": "./node_modules/bower/bin/bower install"
  }

Crear el servidor de archivos estáticos

Imaginemos que nuestra app tiene la siguiente estructura:

/app  --> Archivos fuente
  |--/fonts
  |--/img
  |--/jade
  |--/js
  |--/less
  \--/test
/dev  --> Compilación de desarrollo
  |--/css
  |--/fonts
  |--/html
  |--/img
  |--/js
  \--/test
/pro  --> Compilación de producción (concatenada, minificada y ofuscada)
  |--/css
  |--/fonts
  |--/html
  |--/img
  |--/js
  \--/test

Por defecto, Heroku no nos crea un servidor HTTP. Debemos crearlo nosotros con Node. Para ello nos creamos un archivo javascript al que llamaremos por ejemplo "web.js".

Suponiendo que hemos configurado nuestra app, para que compile los estáticos en la carpeta /dev, y dentro de ella nuestro archivo index se encuentra en html/index.html, este sería nuestro archivo "web.js" para arrancar un servidor de estáticos en Heroku:

web.js

var express = require("express"),
    app = express.createServer(),
    port = process.env.PORT || 3000;

process.env.PWD = process.cwd();

app.configure(function(){
    app.use(express.static(process.env.PWD + '/dev'));
    app.use(express.bodyParser());
    app.use(express.methodOverride());

    //Error Handling
    app.use(express.logger());
    app.use(express.errorHandler({
        dumpExceptions: true, 
        showStack: true
    }));

    app.use(app.router);
});

app.get('/', function(req, res){
    res.redirect("html/index.html");
    res.render("index.html");
});

app.listen(port, function() {
    console.log("Listening on " + port + "\nDirectory: " + process.env.PWD + "/dev");
});

Ahora nos falta decirle a Heroku que ejecute dicho archivo. Para ello debemos crearnos un archivo llamado Procfile. Esto lo explico en el siguiente apartado.

Crear el archivo Procfile

Una vez configurado correctamente package.json, nos creamos un archivo nuevo llamado Procfile, en el que indicamos a Heroku lo que ejecutar al realizar el deploy. Es aquí debemos decirle a Heroku que arranque el servidor web, para que sirva los archivos estáticos generados previamente con grunt. Este sería nuestro Procfile:

web: node web.js

Instalar cliente Heroku en Cloud9

Para poder ejecutar comandos de Heroku, y manejar nuestro entorno desde la terminal de Cloud9, necesitamos instalar el cliente. Para ello escribimos, en el terminal de Cloud9:

wget http://assets.heroku.com/heroku-client/heroku-client.tgz
tar xzfv heroku-client.tgz
cd heroku-client/bin
PATH=$PATH:$PWD

Acceso al bash de Heroku desde Cloud9

Una vez instalado el cliente, desde el terminal de Cloud9 tendremos acceso al bash Heroku. Para ello abrimos un terminal de Cloud9, y escribimos:

heroku run bash -a nombre-de-nuestra-app

Configuramos el Buildpack

Cuando hacemos un git push a nuestro repositorio de Heroku, éste prepara los archivos para ser compilados. El conjunto de acciones que se realizan para compliar, se define en un Buildpack. Heroku dispone de varios Buildpacks, dependiendo del lenguaje y lo que queramos hacer. Sin embargo en este caso, necesitamos un BUILDPACK "no oficial", para que de soporte a Grunt. Para instalar dicho buildpack, escribimos en el terminal:

heroku config:add BUILDPACK_URL=https://github.com/mbuchetics/heroku-buildpack-nodejs-grunt.git -a nombre-de-nuestra-app

En este momento se compilarán nuestros archivos, tal y como lo hubiéramos compilado en Gruntfile.js, y tendremos acceso a ellos desde el servidor web de estáticos, en la URL: http://nombredenuestraapp.herokuapp.com

Añadir deploy en Cloud9

En este apartado veremos cómo lanzar un deploy desde Cloud9:

Después elegimos el servicio Heroku.com:

Configurar el entorno a "development" (opcional)

Este paso es opcional, y explica cómo proceder si quisiéramos cambiar el entorno a desarrollo, en vez de producción que es como lo tiene Heroku por defecto. Desde la terminal de Cloud9 escribimos:

heroku config:set NODE_ENV=development -a nombre-de-nuestra-app
heroku labs:enable user-env-compile -a nombre-de-nuestra-app

Más información

jueves, 25 de julio de 2013

Iniciar un proyecto con Grunt

Introducción

En esta ocasión voy a explicar brevemente los pasos a seguir para realizar un proyecto HTML, CSS, Javascript desde cero. Más concretamente, voy a explicar cómo lo hago yo, que no es ni la mejor ni la peor manera de hacerlo. Simplemente a la que estoy acostumbrado y me va bien. Espero que os ayude a crear vuestra propia metodología de trabajo.

He dejado los archivos en un repositorio de BitBucket: base-grunt. Espero que os sirva!

Voy a suponer que tenemos instalado en nuestro sistema:

  • Bower: gestor de librerías, con el que podemos descargar automáticamente las que necesitemos para el proyecto (bootstrap, jquery, etc.)
  • Grunt; automatizador de tareas
  • Growl: notificaciones de nuestros watchers (para MacOS) (para Windows)
  • Node.js. Sin él, nada sería posible.

Si todo esto te suena a chino, te recomiendo entonces que leas antes el artículo donde explico más en detalle cada una de estas herramientas: Node.js, Grunt.js, Bower y Yeoman.

No comento nada acerca de Yeoman (generador de estructuras de directorios), porque no he sido capaz de hacerlo funcionar correctamente bajo Windows 7, con un generator personalizado.

Creamos el árbol de directorios

Supongamos que tenemos dos entornos: dev (desarrollo) y pro (producción). En desarrollo, guardaremos nuestros archivos ya compilados (CSS, HTML, imágenes y javascript), pero sin minificar, de tal modo que al ejecutarlos, podamos ver fácilmente en qué linea del código se producen los errores. Y también en desarrollo será donde guardemos nuestros test. En producción, guardamos los compilados pero en este caso, con un formato minificado, y a ser posible, concatenando ficheros en uno sólo (todos los CSS en un único archivo), para minificar el número de peticiones al servidor. En dicho caso, la estructura sería:

/proyecto/
|-- app/ <- fuentes
|   |-- coffee/
|   |   `-- commons.coffee
|   |
|   |-- jade/
|   |   |-- inc/
|   |   |   |-- footer.jade
|   |   |   `-- header.jade
|   |   |
|   |   |-- layouts/
|   |   |   `-- main.jade
|   |   |
|   |   `-- index.jade
|   |
|   |-- js/ <- Custom JS sin preprocesar
|   |   `-- app.js
|   |
|   |-- less/
|   |   `-- *.less
|   |
|   `-- test/
|       |-- js/
|       |   `-- test.js
|       |
|       `-- index.jade
|
|-- dev/ <- compilados para desarrollo (sin minificar ni ofuscar)
|
`-- pro/ <- compilados para producción (minificados)

Configuramos las librerías a usar con Bower

Para ello, nos creamos dos ficheros: .bowerrc y component.json. Con el primero, especificamos el directorio donde se desarcarán los repositorios de cada librería, y con el segundo, especificamos qué librerías descargamos, y qué versión de cada una.

En el siguiente ejemplo, indico que vamos a necesitar la última versión de jquery, y la última de QUnit:

.bowerrc

{
  "directory": "bower",
  "json": "bower.json"
}

bower.json

{
  "name": "base-grunt",
  "version": "0.1.0",
  "dependencies": {
    "qunit": "latest",
    "jquery": "latest"
  },
  "ignore": [
    "**/.*",
    ".jshintrc",
    "node_modules",
    "components",
    "bower",
    "test",
    "tests"
  ]
}

Configuramos nuestro package.json y gruntfile.js

El siguiente paso, es añadir nuestro gruntfile.js al proyecto, con la configuración y tareas que deseemos automatizar:

  • Copy: para copiar carpetas y archivos de un lugar a otro
  • JSHint: para revisar nuestro código javascript en busca de errores y malas prácticas
  • Uglify: para minificar código javascript con UglifyJS
  • Notify: para lanzar avisos a Grow
  • Coffeescript: preprocesador javascript
  • Coffeelint: lo mismo que jshint, pero para coffescript: nos ayudará a evitar errores en nuestros archivos coffeescript.
  • Sass: preprocesador CSS, como Less o Stylus. Ya no es necesario tener WinLess corriendo sobre nuestra máquina.
  • Jade, Yaml o Haml: preprocesador HTML. En este caso, voy a utilizar Jade, por la potencia que dan los includes y layouts, que Haml y Yaml no te dan.
  • Watcher: para realizar el compilado automático al guardar el archivo fuente
  • DevTools: para activar las notificaciones con la consola de Chrome

Para ello, tenemos dos archivos: package.json y gruntfile.js. Con el primero, especificamos variables que utilizaremos luego en gruntfile.js, así como las librerías que utilizaremos con Grunt, y detalles del proyecto como el nombre, autor, repositorio, licencias, etc. Con el segundo archivo, gruntfile.js, configuramos las tareas a automatizar, las notificaciones, los entornos, etc.

package.json

{
  "name": "base-grunt",
  "version": "0.1.0",
  "description": "Basic template for build projects with Bower + Grunt + Growl + Less + Jade + CoffeeScript",
  "devDependencies": {
    "matchdep": "latest",
    "grunt": "latest",
    "grunt-coffeelint": "latest",
    "grunt-contrib-jshint": "latest",
    "grunt-contrib-copy": "latest",
    "grunt-contrib-jade": "latest",
    "grunt-contrib-watch": "latest",
    "grunt-notify": "latest",
    "grunt-contrib-less": "latest",
    "grunt-contrib-uglify": "latest",
    "grunt-contrib-coffee": "latest",
    "grunt-devtools": "latest"
  },
  "main": "gruntfile.js",
  "repository": {
    "type": "git",
    "url": "ssh://git@bitbucket.org:alfonsomartinde/base-grunt.git"
  },
  "author": "alfonsomartide",
  "license": "BSD"
}

gruntfile.js

// Lee las propiedades definidas en packaje.json
// Las podemos usar aquí así: <%= pkg.name %>

;(function () {
    "use strict";
    
    module.exports = function(grunt) {

        grunt.initConfig({
        
            pkg: grunt.file.readJSON('package.json'),

            coffeelint: {
                files: {
                    src: ['app/coffee/*.coffee']
                },
                options: {
                    'no_tabs': {'level': 'ignore'},
                    'no_trailing_whitespace': {'level': 'ignore'}
                }
            },

            // app/coffee > dev/js
            coffee: {
                dev: {
                    files: { 
                        'dev/js/commons.js': 'app/coffee/commons.coffee'
                    }
                }
            },

            // dev/js > pro/js
            uglify: {
                pro: {
                    files: {
                        'pro/js/all.min.js':['dev/js/*.js']
                    }
                }
            },

            // Comprobamos errores
            jshint: {
                files: ['gruntfile.js','app/js/**/*.js','app/test/**/*.js'],
                options: {
                    ignores: ['app/js/vendors/*.js'],
                    globals: {
                        jQuery: true,
                        console: true,
                        module: true
                    }
                }
            },


            copy: {
                gen: {
                    files: [
                        // generator > dev
                        {
                            expand: true,
                            cwd: 'app/generator/',
                            src: ['**/*'],
                            dest: 'dev/'
                        },
                        // generator > pro
                        {
                            expand: true,
                            cwd: 'app/generator/',
                            src: ['**/*'],
                            dest: 'pro/'
                        }
                    ]
                },
                dev: {
                    files: [
                        // app/test/js > dev/test/js
                        {
                            expand: true,
                            cwd: 'app/test/js/',
                            src: ['**/*'],
                            dest: 'dev/test/js/'
                        },
                        // app/js/vendors > dev/js/vendors
                        {
                            expand: true,
                            cwd: 'app/js/vendors/',
                            src: ['**/*'],
                            dest: 'dev/js/vendors/'
                        },
                        // app/js > dev/js
                        {
                            expand: true,
                            cwd: 'app/js/',
                            src: ['*.*'],
                            dest: 'dev/js/'
                        },
                        // bower/jquery > dev/js/vendors
                        {
                            expand: true,
                            cwd: 'bower/jquery/',
                            src: ['jquery.min.js'],
                            dest: 'dev/js/vendors/'
                        },
                        // bower/qunit > dev/test/qunit
                        {
                            expand: true,
                            cwd: 'bower/qunit/qunit/',
                            src: ['**/*'],
                            dest: 'dev/test/qunit/'
                        }
                    ]
                },
                pro: {
                    files: [
                        // dev/js/vendors > pro/js/vendors
                        {
                            expand: true,
                            cwd: 'dev/js/vendors/',
                            src: ['**/*'],
                            dest: 'pro/js/vendors/'
                        },
                    ]
                }
            },

            less: {
                dev: {
                    files: {
                        'dev/css/all.css': 'app/less/all.less'
                    }
                },
                pro: {
                    options: {
                        compress: true
                    },
                    files: {
                        'pro/css/all.min.css': 'app/less/all.less'
                    }
                },
            },
    
            jade: {  
                dev: {  
                    options:{  
                        pretty: true,  
                        data: function(){
                            return {developing: "true"};
                        }  
                    },  
                    files:[
                        // app/jade > dev/html
                        {
                            expand: true, 
                            src: "*.jade", 
                            dest: "dev/html/", 
                            ext: ".html", 
                            cwd: "app/jade/"
                        },
                        // app/test > dev/test
                        {
                            expand: true, 
                            src: "*.jade", 
                            dest: "dev/test/", 
                            ext: ".html", 
                            cwd: "app/test/"
                        },
                    ]
                },  
                pro: {  
                    options:{  
                        pretty: false,  
                        data: function(){
                            return {developing: "false"};
                        }
                    },
                    // app/jade > pro/html
                    files:[{
                        expand: true,
                        src: "*.jade",
                        dest: "pro/html/", 
                        ext: ".html", 
                        cwd: "app/jade/"
                    }]
                }  
            }, 

            notify: {
                coffeelint : {
                    options: {
                        enabled: true,
                        max_jshint_notifications: 2,
                        message: "coffeelint iniciado!"
                    }
                },
                coffee : {
                    options: {
                        enabled: true,
                        max_jshint_notifications: 2,
                        message: "coffee iniciado!"
                    }
                },
                jshint: {
                    options: {
                        enabled: true,
                        max_jshint_notifications: 2,
                        message: "jshint iniciado!"
                    }
                },
                uglify: {
                    options: {
                        enabled: true,
                        max_jshint_notifications: 2,
                        message: "uglify iniciado!"
                    }
                },
                jade: {
                    options: {
                        enabled: true,
                        max_jshint_notifications: 2,
                        message: "jade iniciado!"
                    }
                },
                less: {
                    options: {
                        enabled: true,
                        max_jshint_notifications: 2,
                        message: "less iniciado!"
                    }
                }
            },

            watch: {
                coffeelint: {
                    files: ["app/coffee/{,*/}*.coffee"], 
                    tasks: ["notify:coffeelint","coffeelint"]
                },
                coffee: {
                    files: ["app/coffee/{,*/}*.coffee"], 
                    tasks: ["notify:coffee","coffee:dev"]
                },
                jshint: {
                    files: ["app/js/{,*/}*.js","app/test/js/{,*/}*.js"], 
                    tasks: ["notify:jshint","jshint","copy:dev"]
                },
                jade: {
                    files: ["app/jade/{,*/}*.jade","app/test/{,*/}*.jade"],
                    tasks: ["notify:jade","jade:dev"]
                },
                less: {
                    files: ["app/less/{,*/}*.less"],
                    tasks: ["notify:less","less:dev"]
                }
            }

        });


        /**
         * Cargamos todos los tasks declarados en package.json
         *
         */

        require('matchdep')
            .filterDev('grunt-*')
            .forEach(grunt.loadNpmTasks);




        /**
         * Definimos las tareas
         *
         */
        grunt.registerTask("gen", function (target) {
            grunt.task.run([
                "copy:gen"
            ]);
        });

        grunt.registerTask("default", function (target) {
            grunt.task.run([
                "coffeelint",
                "coffee",
                "jshint",
                "uglify", 
                "copy",
                "jade",
                "less",
                "watch",
            ]);
        });

        grunt.registerTask("dev", function (target) {
            grunt.task.run([
                "coffeelint",
                "coffee:dev", 
                "jshint",
                "copy:dev",
                "jade:dev",
                "less:dev",
                "watch",
            ]);
        });

        grunt.registerTask("pro", function (target) {
            grunt.task.run([
                "uglify:pro", 
                "copy:pro",
                "jade:pro",
                "less:pro"
            ]);
        });

    };
}());

Instalando todo

A continuación abrimos la consola MSDOS, nos metemos en el directorio raíz de nuestro proyecto y ejecutamos el comando:

npm install

Hecho esto, se nos habrán descargado las librerías que especificamos en package.json a la carpeta node_modules. El siguiente paso, es descargar las librerías de Bower:

bower install

Si estamos detrás de un proxy, deberemos especificar la variable HTTP_PROXY antes de ejecutar el bower install:

set HTTP_PROXY=http://user:pass@server.url:port

En nuestra carpeta de bower, tendremos descargadas las librerías que hemos indicado. Y con esto, si todo ha ido bien, ya tendremos preparado nuestro entorno de trabajo.

Últimos pasos

Para empezar a programar, sólo nos queda:

  1. Arrancar Growl
  2. Ejecutar la tarea grunt dev, grunt pro, o grunt default, según queramos generar un entorno u otro:
    grunt dev
    

domingo, 7 de julio de 2013

Node.js: qué es y cuándo utilizarlo

Se trata de una "nueva" tecnología del lado del servidor, como pueda ser PHP, Ruby, .NET, etc., basada en el motor de JavaScript V8 de Google: una potente máquina virtual creada por Lars Bak

Ventajas de Node.js a nivel de servidor

Una ventaja de Node.js es el lenguaje con el que se crean sus aplicaciones: JavaScript. Si! Ahora podemos realizar consultas a base de datos, lectura de ficheros, procesamiento de imágenes, todo con javascript en el lado del servidor. Esto está haciendo que JavaScript, sea el lenguaje del momento, como lo fue PHP en su día, o Ruby.

Utilizando un servidor web tradicional, como pueda ser Apache, cada vez que solicitas un recurso web, éste crea un hilo separado, o invoca un nuevo proceso, para atender dicha solicitud. A pesar de que Apache responde rápidamente a las solicitudes, y limpia el proceso una vez que ha terminado, este sistema puede necesitar un montón de recursos. Una aplicación web con muchas visitas, puede tener serios problemas de rendimiento.

Node.js, por el contrario, utiliza un sistema de E/S asíncrono y basado en eventos y callbacks de funciones. Es decir, todo lo realiza en un único hilo: se queda escuchando a ciertos eventos que ocurran en nuestra aplicación, y cuando ocurren, responde en consecuencia, de modo asíncrono, es decir, un evento no bloquea a otro. Dicho de otro modo, crea miles de subprocesos y trata a sus outputs como streams. Esto supone una mayor velocidad de respuesta. Os dejo un enlace donde podéis comprobar vosotros mismos, que Node.js es muchísimo más rápido sirviendo peticiones que Apache: https://code.google.com/p/node-js-vs-apache-php-benchmark/wiki/Tests

Ventajas de Node.js a nivel de desarrollador

Dejo para el final, la que para mi supone a día de hoy la principal ventaja: tener Node.js instalado en tu entorno de desarrollo local, te abre la puerta a una inmensa cantidad de herramientas, con las que automatizar tediosos procesos y mejorar tu rendimiento como desarrollador: es decir, te permite centrarte en el desarrollo y olvidarte de lo demás. Independientemente del lenguaje que uses después, con Node.js instalado en local, puedes utilizar:

  • Grunt.js: imprescindible procesador de tareas, y watchers, con el que automatizar el compilado de tus archivos a varios entornos. Es decir, podemos trabajar con preprocesadores como Sass, Haml, CoffeScript, etc., con la ventaja que esto ya supone, pero además, unificar todos sus watchers en un único lugar, y compilar varias salidas al mismo tiempo, en diferentes formatos, según lo necesitemos. Por ejemplo, si trabajas con Sass, Less o Stylus, puedes decidir compilar para producción un CSS minificado, y al mismo tiempo, compilar para un entorno de desarrollo, otro sin minificar en otra carpeta. Con Grunt puedes también usar JSHint, que en tiempo real irá diciéndote si en tus .js estás cometiendo errores sintácticos, en qué linea, y cómo resolverlos. Y si a la vez le pones Haml, Yaml, Ugligy, etc. pues lo mismo, puedes minificar la salida para producción, pero para tu entorno de pruebas, dejarlo sin minificar. Más información sobre Grunt.
  • Bower: gestor de paquetes o librerías desarrollado por Twitter. Cada vez que inicias un proyecto, Bower se encargará por ti, de descargar las versiones que necesites de las librerías javascript que necesites, o si lo prefieres, que descargue siempre la última versión, y coloque los archivos donde tu le digas: jQuery, bootstrap, CoffeeScript, etc.: aquí tenéis toda la lista de paquetes: http://sindresorhus.com/bower-components/. Más información sobre Bower.
  • Yo: un paquetizador de entornos, por decirlo de alguna manera, que se integra con Grunt. Te permite configurar el árbol de directorios de cada nuevo proyecto como a ti te guste: él te crea la estructura por ejemplo, para un proyecto con HTML5, o PHP. Aún no he conseguido que funcione demasiado bien bajo Windows, y estoy en búsqueda de alternativas... Más información sobre Yo.
  • All together now!: Yeoman, un conjunto de herramientas integradas entre sí: el gestor de entornos Yo, el gestor de librerías Bower, y el automatizador de tareas Grunt. Más información sobre Yeoman.

¿Cúando utilizar Node.js?

Existen varios casos donde no tiene sentido utilizar Node.js. Por ejemplo, cuando nuestra aplicación necesite muchísima CPU, y tenga pocos procesos de E/S, como podría ser, codificando vídeo. Para eso, casi mejor utilizar otros lenguajes como C o C++.

Otro ejemplo donde no vamos a ver una gran ventaja utilizando Node.js, es si estamos programando una aplicación clásica contra base de datos: alta, baja, edición y modificación de registros. Aquí, Node.js no te va a dar más ventajas de las que te pueda dar PHP, o Ruby, ya que trabajarás con un único canal de E/S.

Donde si es adecuado Node.js, es en aplicaciones de una sola página, con mucho AJAX, o cuando necesitas hacer muchas cosas al mismo tiempo, sobre todo muchas operaciones E/S (acceso a ficheros, bases de datos,...) a la vez.

También obtendrás una gran ventaja en velocidad y rendimiento, con aplicaciones en tiempo real, que necesitan mantener una conexión persistente entre el navegador y el servidor (juegos online, chats, herramientas de colaboración, etc ).

Otro punto interesante, es que para Node.js, las peticiones y respuestas HTTP son streams, lo que supone poder parsear subidas de archivo en tiempo real: http://nodejs.org/api/stream.html

Conclusiones

Por supuesto, no a todo el mundo le gusta Node.js, especialmente cuando se convierte en un lenguaje de moda. Hace poco leí en un post que las gráficas de rendimiento y velocidad, se hacen contra Apache, y no contra PHP con Nginx, para favorecer a Node.js, y que éste último, era inseguro. Obviamente, sus conclusiones tampoco eran del todo ciertas: Nginx es un servidor web/proxy inverso, y puede ser usado también con Node.js. Es decir, para ser justos, habría que comparar PHP/Nginx, con Node/Nginx. Y respecto a la seguridad, depende del desarrollador, no de la herramienta.

Está claro, que cada tecnología, tiene su sentido en un determinado contexto, y hay que saber aplicar la mejor según las necesidades del proyecto. Supongo que por esto es importante conocer bien qué opciones tenemos, para ofrecer siempre el mejor servicio, y no quedarse atascado en tecnologías clásicas. Dicho de otro modo: todos los caminos conducen a Roma, pero por unos llegas antes que por otros. Cuantas más opciones conozcas, mejor para ti como desarrollador.