React.js

Añadir React.js a una aplicación Node.js

Vamos a ver cuál es la mejor forma de conectar nuestra aplicación Node.js con React.js.

Para ello, necesitamos instalar el paquete facebook/create-react-app de manera global:

sudo npm install -g create-react-app

Después, en la raíz de nuestro proyecto, creamos una carpeta llamada client, donde vamos a tener todo el frontend de la aplicación escrito con React:

create-react-app client

Hecho esto, vemos que tenemos ya un boilerplate de React dentro del directorio client.

Esta aplicación de React que acabamos de instalar ya trae su propio servidor para servir la aplicación.

Y surge la pregunta: ¿vamos a trabajar con ambos servidores a la vez? Sí.

Modo de desarrollo: utilizando dos servidores a la vez (React y Express)

Fijándonos en el siguiente diagrama, vemos que el servidor de React se va a encargar de servir los archivos JavaScript necesarios para mostrar las vistas, mienras que el servidor Express se va a encargar de recuperar los datos procedentes de la base de datos que necesiten los usuarios.

Servidores React y Express

Y puede que surja otra pregunta: ¿por qué no tenemos un único servidor Express que se encargue también de manejar la aplicación basada en React?

La única razón por la que no vamos a hacer eso es por toda la configuración que ya trae consigo creat-react-app, lo cual nos va a ahorrar mucho tiempo a la hora de desarrollar nuestra aplicación.

Cómo lanzar ambos servidores a la vez

Para poder lanzar ambos servidores, vamos a instalar un paquete llamado concurrently, que nos va a permitir ejecutar ambos servidores con un solo comando:

npm install -S concurrently

Ahora, modificamos los scripts dentro de package.json, para que queden de esta manera:

"scripts": {
  "start": "node index.js",
  "server": "nodemon index.js",
  "client": "npm run start --prefix client",
  "dev": "concurrently \"npm run server\" \"npm run client\""
},

Y ya podemos lanzar los dos servidores con un mismo comando:

npm run dev

Cómo utilizar las rutas entre servidores

Llegados a este punto, puede que nos encontremos con el siguiente problema. Imagina que dentro de tu aplicación React (http://localhost:3000) tienes un enlace a tu aplicación Express (http://localhost:5000).

Para que la petición llegue correctamente al otro servidor, deberás incluir la URL absoluta. Por ejemplo, si queremos acceder a la ruta /auth/google de nuestro backend, deberemos escribir http://localhost:5000/auth/google.

El problema aquí es que no queremos estar escribiendo por todos lados el dominio de nuestro servidor, en este caso http://localhost:5000, ya que cuando subamos el código a producción tendríamos que cambiar todo esto...

La solución a este problema es utilizar un proxy. Vamos a ello.

  1. Dentro de /client, instalamos este paquete:

npm install -S http-proxy-middleware

  1. Creamos el archivo /client/src/setupProxy.js. No hace falta que importemos este archivo en ningún sitio, ya que create-react-app se encargará de buscarlo y leerlo automáticamente.
const proxy = require('http-proxy-middleware')

module.exports = function(app) {
  app.use(proxy('/auth/google', {
    target: 'http://localhost:5000',
  }))
}

Y ya podemos utilizar sin problemas URL relativas para acceder a nuestro servidor backend.

Cómo funciona esta arquitectura

Arquitectura del entorno de desarrollo

En nuestro entorno de desarrollo, cuando accedemos a http://localhost:3000, create-react-app nos va a devolver el archivo bundle.js, donde está toda la aplicación de React.

Sin embargo, cuando queramos acceder a alguna ruta de la API, vamos a utilizar un proxy incluido dentro de create-react-app para poder mandar la petición al servidor Express.

Modo de producción

En el entorno de producción, las cosas van a ser un poco diferentes.

Arquitectura del entorno de producción

Como podemos ver, en producción no va a existir el servidor creado por create-react-app, así que no vamos a utilizar el proxy creado anteriormente.

Lo que va a pasar es que, antes de desplegar la aplicación, create-react-app va a empaquetar toda la aplicación React, -utilizando Webpack y Babel-, dentro de /client/build (utilizando el comando npm run build).

Entonces, cuando simplemente accedamos al sitio web, el servidor Node/Express nos va a responder con lo que encuentre dentro de la carpeta /client/build; y cuando accedamos a alguna ruta de la API, al estar escritas con URL relativas, el servidor ya va a saber cómo manejarlas.

Explicación de la arquitectura utilizada

Hasta ahora hemos visto que la arquitectura utilizada es muy elegante y funciona realmente bien tanto en el entorno de desarrollo como en producción.

Sin embargo, puede que pensemos que esta arquitectura es un poco engorrosa...

Vamos a ver ciertas críticas que, en un principio, pueden poner en duda la arquitectura empleada, y después algunos argumentos que hacen que esta arquitectura sea la ideal.

Para empezar, podemos plantear otra posible arquitectura, donde tenemos los servidores React y Express totalmente separados.

Otra arquitectura posible

Cookies

La primera razón por la que no deberíamos implementar esta arquitectura está relacionada con el uso de cookies.

Uso de cookies

Si nuestra aplicación utiliza cookies, -por ejemplo para manejar la autenticación de los usuarios-, el navegador, -como medida de seguridad-, va a dejar de incluir las cookies en las peticiones al servidor que actúe como API, ya que el dominio del navegador no corresponde con la de este servidor.

Uso de un proxy en el entorno de desarrollo

En nuestro entorno de desarrollo hemos venido utilizando también dos servidores, los cuales se comunican a través de un proxy.

En este caso, el problema de las cookies no afecta en absoluto, ya que el navegador siempre envía las peticiones al Server #1, y éste, a través de un proxy, es capaz de redirigir al Server #2 las peticiones que vayan a él.

Por lo tanto, el proxy recibe las cookies con la petición, y envía todo al Server #2.

CORS

La segunda razón por la que no deberíamos implementar esta arquitectura está relacionada con las peticiones CORS.

Peticiones CORS

Al realizar peticiones AJAX a un dominio diferente, el navegador siempre va a mostrar algún error de seguridad por tratarse de una petición CORS.

Esto tiene solución, y es configurando el servidor localhost:5000 para que acepte peticiones CORS.

Uso de un proxy en el entorno de desarrollo

Otra vez más, en nuestro entorno de desarrollo, el uso de dos servidores no causa ningún error por peticiones CORS, ya que el navegador siempre se comunica con el Server #1, y luego éste se comunica con el Server #2 a través de un proxy, y esta comunicación entre servidores no se ve afectada por CORS.

Configurar React

Vamos a ver cómo configurar React desde cero.

Instalar Redux y React Router

Antes de nada, vamos a empezar viendo dos archivos, que van a ser la base de la aplicación.

  • index.js: Es el primer archivo que se va a ejecutar en el navegador. Contiene las capas de arranque (React) y de datos (Redux).
  • App.js: El componente raíz. Contiene la capa de representación (React Router). Se va a encargar de renderizar los componentes correspondientes de la vista que vamos a mostrar.

Ahora sí, instalamos las librerías necesarias:

cd client
npm install -S redux react-redux react-router-dom

Y creamos los archivos index.js y App.js:

// /client/src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';

import App from './components/App';
import reducers from './reducers';

const store = createStore(reducers, {}, applyMiddleware());

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.querySelector('#root')
);
// /client/src/components/App.js

import React from 'react';

const App = () => {
  return (
    <div>
      Hi There!
    </div>
  );
};

export default App;