Tutorial

Cómo crear un servidor web en Node.js con el módulo HTTP

Node.jsDevelopmentJavaScriptProgramming Project

El autor seleccionó el COVID-19 Relief Fund para que reciba una donación como parte del programa Write for DOnations.

Introducción

Cuando visualiza una página web en su navegador, está realizando una solicitud a otro equipo en Internet, que a continuación proporciona la página web como respuesta. Esa computadora con la que está hablando a través de Internet es un servidor web. Un servidor web recibe solicitudes HTTP de un cliente, como su navegador, y proporciona una respuesta HTTP, como una página HTML o JSON desde una API.

Para que un servidor devuelva una página web, se emplea una gran cantidad de software. Este software generalmente se divide en dos categorías: frontend y backend. El código front-end se refiere a cómo se presenta el contenido, como el color de la barra de navegación y el estilo de texto. El código back-end se encarga de la forma en la que los datos se intercambian, procesan y almacenan. El código que administra las solicitudes de red desde su navegador o se comunica con la base de datos lo gestiona principalmente el código back-end.

Node.js permite a los desarrolladores usar JavaScript para escribir código back-end, aunque tradicionalmente se usaba en el navegador para escribir código front-end. Al tener el frontend y el backend juntos de esta forma, se reduce el esfuerzo necesario para crear un servidor web, que es el motivo principal por el que Node.js es una opción popular para escribir código back-end.

En este tutorial, aprenderá cómo construir servidores web usando el módulo http que se incluye en Node.js. Creará servidores web que pueden devolver datos JSON, archivos CSV y páginas web HTML.

Requisitos previos

Paso 1: Crear un servidor HTTP básico

Comenzaremos creando un servidor que devuelve texto sin formato al usuario. Esto cubrirá los conceptos clave necesarios para configurar un servidor, que proporcionará la base necesaria para devolver formatos de datos más complejos como JSON.

Primero, debemos configurar un entorno de codificación accesible para hacer nuestros ejercicios y los demás en este artículo. En el terminal, cree una carpeta llamada first-servers:

  • mkdir first-servers

Luego, acceda a esa carpeta:

  • cd first-servers

Ahora, cree el archivo en donde se alojará el código:

  • touch hello.js

Abra el archivo en un editor de texto. Usaremos nano, ya que está disponible en el terminal:

  • nano hello.js

Comenzaremos cargando el módulo http que es estándar con todas las instalaciones de Node.js. Añada la siguiente línea a hello.js:

first-servers/hello.js
const http = require("http");

El módulo http contiene la función para crear el servidor, que veremos más adelante. Si desea obtener más información sobre los módulos en Node.js, consulte nuestro artículo Cómo crear un módulo Node.js.

Nuestro siguiente paso será definir dos constantes, el host y el puerto a los que se vinculará nuestro servidor:

first-servers/hello.js
...
const host = 'localhost';
const port = 8000;

Como mencionamos antes, los servidores web aceptan solicitudes de los navegadores y de otros clientes. Podemos interactuar con un servidor web ingresando un nombre de dominio, que se traduce a una dirección IP mediante un servidor DNS. Una dirección IP es una secuencia única de números que identifica un equipo en una red, como Internet. Para obtener más información sobre los conceptos de nombre de dominio, eche un vistazo a nuestro artículo Una introducción a la terminología DNS, componentes y conceptos.

El valor localhost es una dirección privada especial que los ordenadores usan para referirse a ellos mismos. Normalmente, es el equivalente a la dirección IP interna 127.0.0.1 y solo está disponible para el equipo local, no para cualquier otra red a la que nos unamos o a Internet.

El puerto es un número que los servidores usan como endpoint o “puerta” a nuestra dirección IP. En nuestro ejemplo, usaremos el puerto 8000 para nuestro servidor web. Los puertos 8080 y 8000 se usan normalmente como puertos predeterminados en desarrollo, y, en la mayoría de los casos, los desarrolladores los usarán en vez de los otros puertos para los servidores HTTP.

Cuando vinculamos nuestro servidor a este host y puerto, podremos conectarnos a nuestro servidor visitando http://localhost:8000 en un navegador local.

Ahora, añadiremos una función especial, que en Node.js llamamos una escucha de solicitudes. Esta función está destinada a gestionar una solicitud HTTP entrante y devolver una respuesta HTTP. Esta función debe tener dos argumentos, un objeto de solicitud y un objeto de respuesta. El objeto de solicitud captura todos los datos de la solicitud HTTP que está entrando. El objeto de respuesta se usa para devolver respuestas HTTP para el servidor.

Queremos que nuestro primer servidor devuelva este mensaje siempre que alguien acceda a él: "My first server!".

Ahora agreguemos esa función:

first-servers/hello.js
...

const requestListener = function (req, res) {
    res.writeHead(200);
    res.end("My first server!");
};

La función normalmente recibe su nombre según lo que hace. Por ejemplo, si creamos una función de escucha de solicitudes para devolver una lista de libros, probablemente la llamaremos listBooks(). Ya que este es un ejemplo, usaremos el nombre genérico requestListener.

Todas las funciones de escucha de solicitudes en Node.js aceptan dos argumentos: req y res (podemos llamarlos de forma diferente si lo deseamos). La solicitud HTTP que el usuario envía se captura en un objeto Request, que se corresponde con el primer argumento, req. La respuesta HTTP que devolvemos al usuario se forma interactuando con el objeto Response en el segundo argumento, res.

La primera línea res.writeHead(200); establece el código de estado HTTP de la respuesta. El código de estado HTTP indica si la solicitud HTTP fue gestionada correctamente por el servidor. En este caso, el código de estado 200 se corresponde con "OK". Si está interesado en aprender sobre los diferentes códigos HTTP que sus servidores web pueden devolver con el significado que tienen, nuestra guía Cómo resolver códigos de error HTTP comunes es un buen comienzo.

La siguiente línea de la función, res.end("My first server"!) ; escribe la respuesta HTTP de vuelta al cliente que la solicitó. Esta función devuelve cualquier dato que el servidor tenga para devolver. En este caso, está devolviendo datos de texto.

Finalmente, ahora podemos crear nuestro servidor y usar nuestra escucha de solicitudes.

first-servers/hello.js
...

const server = http.createServer(requestListener);
server.listen(port, host, () => {
    console.log(`Server is running on http://${host}:${port}`);
});

Guarde y salga de nano pulsando CTRL+X.

En la primera línea, creamos un nuevo objeto server a través de la función createServer() del módulo http. Este servidor acepta solicitudes HTTP y las pasa a nuestra función requestListener().

Tras crear nuestro servidor, debemos vincularlo a una dirección de red. Hacemos eso con el método server.listen(). Acepta tres argumentos: puerto, host y una función de llamada que se activa cuando el servidor empieza a escuchar.

Todos estos argumentos son opcionales, pero es una buena idea indicar explícitamente qué puerto y host queremos que use el servidor web. Cuando se implementan servidores web en entornos diferentes, conocer el puerto y el host en los que se está ejecutando es necesario para establecer el equilibrio de carga o un alias de DNS.

La función callback registra un mensaje en nuestra consola para que sepamos cuándo el servidor comenzó a escuchar conexiones.

Nota: Aunque requestListener() no usa el objeto req, debe seguir siendo el primer argumento de la función.

Con menos de quince líneas de código, ahora tenemos un servidor web. Veámoslo en acción y hagamos una prueba de extremo a extremo ejecutando el programa:

  • node hello.js

En la consola, veremos este resultado:

Output
Server is running on http://localhost:8000

Observe que la solicitud desaparece. Esto es porque un servidor Node.js es un proceso largo de ejecución. Solo existe si se encuentra un error que haga que se detenga y se cierre, o si detenemos el proceso de Node.js ejecutando el servidor.

En una ventana de terminal independiente, nos comunicaremos con el servidor usando cURL, una herramienta CLI para transferir datos hacia y desde una red. Ingrese el comando para realizar una solicitud GET HTTP a nuestro servidor en ejecución:

  • curl http://localhost:8000

Cuando pulsamos ENTER, nuestro terminal mostrará el siguiente resultado:

Output
My first server!

Ahora hemos configurado y servidor, y tenemos nuestra primera respuesta del servidor.

Desglosemos lo que sucedió cuando probamos nuestro servidor. Usando cURL, enviamos una solicitud GET al servidor en http://localhost:8000. Nuestro servidor Node.js escuchó las conexiones de esa dirección. El servidor pasó esa solicitud a la función requestListener(). La función devolvió datos de texto con el código de estado 200. El servidor, luego, envió esa respuesta de vuelta a cURL, que mostró el mensaje en nuestro terminal.

Antes de continuar, vamos a salir de nuestro servidor en ejecución pulsando CTRL+C. Esto interrumpe la ejecución de nuestro servidor, lo que nos lleva de regreso a la línea de comandos.

En la mayoría de los sitios web que visitamos o las APIs que usamos, las respuestas del servidor son raramente texto sin formato. Obtenemos páginas HTML y datos JSON como formatos de respuesta comunes. En el siguiente paso, aprenderá cómo devolver respuestas HTTP en formatos de datos comunes que encontramos en la web.

Paso 2: Devolver tipos de contenido diferentes

La respuesta que devolvemos de un servidor web puede tener varios formatos. Hemos mencionado JSON y HTML, y podemos devolver otros formatos de texto como XML y CSV. Finalmente, los servidores web pueden devolver datos que no son texto, como PDFs, archivos comprimidos, audio y vídeo.

En este artículo, además del texto sin formato que acabamos de devolver, aprenderá cómo devolver los siguientes tipos de datos:

  • JSON
  • CSV
  • HTML

Estos tres tipos de datos están basados en texto y son formatos populares para entregar contenido en la web. Muchos lenguajes y herramientas de desarrollo del lado del servidor pueden devolver estos diferentes tipos de datos. En el contexto de Node.js, necesitamos hacer dos cosas:

  1. establecer el encabezado Content-Type en nuestras respuestas HTTP con el valor apropiado.
  2. asegurarnos de que res.end() obtiene los datos en el formato adecuado.

Veamos esto en acción con algunos ejemplos. El código que escribiremos en esta sección y en las posteriores tienen muchas similitudes con el código que escribimos previamente. La mayoría de los cambios existen en la función requestListener(). Vamos a crear archivos con este “código de plantilla” para hacer que las siguientes secciones sean más fáciles de seguir.

Cree un nuevo archivo llamado html.js. Este archivo se usará más adelante para devolver texto HTML en una respuesta HTTP. Pondremos el código de la plantilla aquí y lo copiaremos a los otros servidores que devuelven varios tipos.

En el terminal, ingrese lo siguiente:

  • touch html.js

Ahora abra este archivo en un editor de texto:

  • nano html.js

Vamos a copiar el “código de la plantilla”. Ingrese esto en nano:

first-servers/html.js
const http = require("http");

const host = 'localhost';
const port = 8000;

const requestListener = function (req, res) {};

const server = http.createServer(requestListener);
server.listen(port, host, () => {
    console.log(`Server is running on http://${host}:${port}`);
});

Guarde y salga de html.js con CTRL+X, luego vuelva al terminal.

Ahora, vamos a copiar este archivo en dos nuevos archivos. El primero archivo será para devolver datos CSV en la respuesta HTTP:

  • cp html.js csv.js

El segundo archivo devolverá una respuesta JSON en el servidor:

  • cp html.js json.js

Los archivos restantes serán para ejercicios posteriores:

  • cp html.js htmlFile.js
  • cp html.js routes.js

Ahora estamos listos para continuar con nuestros ejercicios. Vamos a comenzar con devolver JSON.

Presentar JSON

JavaScript Object Notation, normalmente conocido como JSON, es un formato de intercambio de datos basado en texto. Como su nombre lo indica, se deriva de los objetos JavaScript, pero es un lenguaje independiente, lo que significa que puede ser usado por cualquier lenguaje de programación que pueda analizar su sintaxis.

Las APIS comúnmente usan JSON para aceptar y devolver datos. Su popularidad se debe al menor tamaño de transferencia de datos que los estándares de intercambio de datos como XML, además de las herramientas que existen para permitir que los programas las analicen sin esfuerzo excesivo. Si desea obtener más información sobre JSON, puede leer nuestra guía sobre Cómo trabajar con JSON en JavaScript.

Abra el archivo json.js con nano:

  • nano json.js

Queremos devolver una respuesta JSON. Ahora, modifiquemos la función requestListener() para devolver al encabezado apropiado todas las respuestas que JSON tiene cambiando las líneas resaltadas de esta forma:

first-servers/json.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
};
...

El método res.setHeader() añade un encabezado HTTP a la respuesta. Los encabezados HTTP son información adicional que puede adjuntarse a una solicitud o respuesta. El método res.setHeader() toma dos argumentos: el nombre del encabezado y su valor.

El encabezado Content-Type se usa para indicar el formato de los datos, también conocido como tipo de medio, que se envían con la solicitud o respuesta. En este caso, nuestro Content-Type es application/json.

Ahora vamos a devolver contenido JSON al usuario. Modifique json.js para que tenga este aspecto:

first-servers/json.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
    res.writeHead(200);
    res.end(`{"message": "This is a JSON response"}`);
};
...

Como antes, indicamos al usuario que su solicitud se realizó correctamente devolviendo un código de estado de 200. Esta vez, en la invocación response.end(), nuestro argumento de cadena contiene JSON válido.

Guarde y salga de jason.js pulsando CTRL+X. Ahora, ejecutaremos el servidor con el comando node:

  • node json.js

En otro terminal, vamos a contactar con el servidor usando cURL:

  • curl http://localhost:8000

Cuando pulsemos ENTER, veremos el siguiente resultado:

Output
{"message": "This is a JSON response"}

Ahora tenemos una respuesta JSON devuelva, como muchas de las APIs populares con las que creamos aplicaciones. Asegúrese de salir del servidor en ejecución con CTRL+C para poder volver a la solicitud estándar del terminal. Veamos otro formato popular para devolver datos: CSV.

Presentar CSV

El formato de archivo Valores separados por coma (CSV) es un estándar de texto que se usa comúnmente para proporcionar datos tabulares. En la mayoría de los casos, cada fila se separa por una nueva línea, y cada elemento de la fila se separa por una coma.

En nuestro espacio de trabajo, abra el archivo csv.js en un editor de texto:

  • nano csv.js

Agreguemos las siguientes líneas a nuestra función requestListener():

first-servers/csv.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "text/csv");
    res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv");
};
...

Esta vez, nuestro Content-Type indica que se está devolviendo un archivo CSV cuyo valor es text/csv. El segundo encabezado que añadimos es Content-Disposition. Este encabezado indica al navegador cómo mostrar los datos, sobre todo en el navegador o como archivo independiente.

Cuando devolvemos respuestas CSV, la mayoría de los navegadores modernos descargan automáticamente el archivo, incluso si el encabezado Content-Disposition no se ha establecido. Sin embargo, cuando se devuelve un archivo CSV, deberíamos añadir este encabezado, ya que nos permite establecer el nombre del archivo CSV. En este caso, indicamos al navegador que este archivo CSV es un adjunto y debería descargarse. A continuación, indicamos al navegador que el nombre del archivo es oceanpals.csv.

Vamos a escribir los datos CSV en la respuesta HTTP:

first-servers/csv.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "text/csv");
    res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv");
    res.writeHead(200);
    res.end(`id,name,email\n1,Sammy Shark,shark@ocean.com`);
};
...

Como antes, devolvemos un estado 200/OK con nuestra respuesta. Esta vez, nuestra invocación de res.end() tiene una cadena que es un CSV válido. La coma separa el valor en cada columna y carácter de nueva línea (\n) separa las filas. Tenemos dos, una para el encabezado de tabla y una para los datos.

Probaremos este servidor en el navegador. Guarde csv.js y salga del editor con CTRL+X.

Ejecute el servidor con el comando Node.js:

  • node csv.js

En otro terminal, vamos a contactar con el servidor usando cURL:

  • curl http://localhost:8000

La consola mostrará esto:

Output
id,name,email 1,Sammy Shark,shark@ocean.com

Si vamos a http://localhost:8000 en nuestro navegador, se descargará un archivo CSV. Su nombre de archivo será oceanpals.csv.

Salga del servidor en ejecución con CTRL+C para volver a la solicitud estándar del terminal.

Al haber devuelto JSON y CSV, cubrimos dos casos que son populares para las APIs. Ahora pasemos a cómo datos para los sitios web que las personas visitan en un navegador.

Presentar HTML

HTML, o Lenguaje de marcado de hipertexto, es el formato más común usado cuando queremos que los usuarios interactúen con nuestros servidores a través de un navegador web. Se creó para estructurar el contenido web. Los navegadores web se crearon para mostrar contenido HTML y cualquier estilo que añadamos con CSS, otra tecnología web de front-end que nos permite cambiar la estética de nuestros sitios web.

Volvamos a abrir html.js con nuestro editor de texto:

  • nano html.js

Modifique la función requestListener() para devolver el encabezado Content-Type apropiado para una respuesta HTML:

first-servers/html.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "text/html");
};
...

Ahora devolvamos el contenido HTML al usuario. Añada las líneas resaltadas a html.js para que tenga este aspecto:

first-servers/html.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "text/html");
    res.writeHead(200);
    res.end(`<html><body><h1>This is HTML</h1></body></html>`);
};
...

Primero añadimos el código de estado HTTP. Luego, invocamos response.end() con un argumento de cadena que contiene un HTML válido. Cuando accedamos a nuestro servidor en el navegador, veremos una página HTML con una etiqueta de encabezado que contiene This is HTML.

Vamos a guardarla y a salir pulsando CTRL+X. Ahora, ejecutaremos el servidor con el comando node:

  • node html.js

Veremos que server se está ejecutando en http://localhost:8000 cuando nuestro programa se ha iniciado.

Ahora, vaya al navegador y visite http://localhost:8000. Nuestra página tendrá este aspecto:

Imagen de la respuesta HTML devuelva desde el servidor de Node.js

Salgamos del servidor en ejecución con CTRL+C para volver a la solicitud estándar del terminal.

Es común que HTML se escriba en un archivo, separado del código del servidor como nuestros programas Node.js. A continuación, veamos cómo podemos devolver respuestas HTML desde los archivos.

Paso 3: Presentar una página HTML desde un archivo

Podemos presentar HTML como cadenas en Node.js para el usuario, pero es preferible que carguemos los archivos HTML y presentemos su contenido. De esta forma, a medida que el archivo HTML crece, no tenemos que mantener cadenas largas en nuestro código Node.js, manteniéndolo más conciso y permitiéndonos trabajar en cada aspecto de nuestro sitio web de forma independiente. Esta “separación de dudas” es común en muchas configuraciones de desarrollo web, y, por tanto, es bueno saber cómo cargar archivos HTML para que sean compatibles en Node.js.

Para presentar archivos HTML, cargamos el archivo HTML con el módulo fs y usamos sus datos cuando escribamos nuestra respuesta HTTP.

Primero, crearemos un archivo HTML que el servidor web devolverá. Cree un nuevo archivo HTML:

  • touch index.html

Ahora abra index.html en un editor de texto:

  • nano index.html

Nuestra página web será mínima. Tendrá un fondo naranja y mostrará algún texto de bienvenida en el centro. Añada este código al archivo:

first-servers/index.html
<!DOCTYPE html>

<head>
    <title>My Website</title>
    <style>
        *,
        html {
            margin: 0;
            padding: 0;
            border: 0;
        }

        html {
            width: 100%;
            height: 100%;
        }

        body {
            width: 100%;
            height: 100%;
            position: relative;
            background-color: rgb(236, 152, 42);
        }

        .center {
            width: 100%;
            height: 50%;
            margin: 0;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: white;
            font-family: "Trebuchet MS", Helvetica, sans-serif;
            text-align: center;
        }

        h1 {
            font-size: 144px;
        }

        p {
            font-size: 64px;
        }
    </style>
</head>

<body>
    <div class="center">
        <h1>Hello Again!</h1>
        <p>This is served from a file</p>
    </div>
</body>

</html>

Esta página web muestra dos líneas de texto: Hello Again! y This is served from a file. Las líneas aparecen en el centro de la página, una sobre otra. La primera línea de texto se muestra en un encabezado, lo que significa que será grande. La segunda línea de texto aparecerá ligeramente más pequeña. Todo el texto aparecerá en blanco y la página web tiene un fondo naranja.

Aunque no está dentro del alcance de este artículo o serie, si está interesado en aprender más sobre HTML, CSS y otras tecnologías web front-end, puede consultar la guía Primeros pasos con la Web de Mozilla.

Eso es todo lo que necesitamos para el HTML, así que guarde y cierre el archivo con CTRL+X. Ahora podemos pasar al código del servidor.

Para este ejercicio, trabajaremos en htmlFile.js. Ábralo con el editor de texto:

  • nano htmlFile.js

Como tenemos que leer un archivo, comenzaremos importando el módulo fs:

first-servers/htmlFile.js
const http = require("http");
const fs = require('fs').promises;
...

Este módulo contiene una función readFile() que usaremos para cargar el archivo HTML. Importamos la variable de promesas de acuerdo con las mejores prácticas del JavaScript moderno. Usamos promesas ya que son sintácticamente más precisas que las invocaciones, que tendríamos que usar si asignamos fs a solo require('fs'). Para aprender más sobre las buenas prácticas de la programación asíncrona, puede leer nuestra guía Cómo escribir código asíncrono en Node.js.

Queremos que nuestro archivo HTML se lea cuando un usuario solicite nuestro sistema. Comencemos modificando requestListener() para leer el archivo:

first-servers/htmlFile.js
...
const requestListener = function (req, res) {
    fs.readFile(__dirname + "/index.html")
};
...

Usamos el método fs.readFile() para cargar el archivo. Su argumento tiene __dirname + "/index.html". La variable especial __dirname tiene la ruta absoluta de donde se está ejecutando el código Node.js. Luego, adjuntamos /index.html para que podamos cargar el archivo HTML que creamos antes.

Ahora, devolvamos la página HTML cuando se cargue:

first-servers/htmlFile.js
...
const requestListener = function (req, res) {
    fs.readFile(__dirname + "/index.html")
        .then(contents => {
            res.setHeader("Content-Type", "text/html");
            res.writeHead(200);
            res.end(contents);
        })
};
...

Si la promesa fs.readFile() se resuelve correctamente, devolverá sus datos. Usamos el método then() para gestionar este caso. El parámetro contents contiene los datos del archivo HTML.

Primero establecemos el encabezado Content-Type a text/html para indicar al cliente que estamos devolviendo datos HTML. Luego, escribimos el código de estado para indicar que la solicitud se realizó correctamente. Finalmente, enviamos al cliente la página HTML que cargamos, con los datos en la variable contents.

El método fs.readFile() puede fallar a veces, de modo que deberíamos gestionar este caso cuando obtengamos un error. Añada esto a la función requestListener():

first-servers/htmlFile.js
...
const requestListener = function (req, res) {
    fs.readFile(__dirname + "/index.html")
        .then(contents => {
            res.setHeader("Content-Type", "text/html");
            res.writeHead(200);
            res.end(contents);
        })
        .catch(err => {
            res.writeHead(500);
            res.end(err);
            return;
        });
};
...

Guarde el archivo y salga de nano con CTRL+X.

Cuando se produce un error con una promesa, se rechaza. Gestionamos ese caso con el método catch(). Acepta el error que fs.readFile() devuelve, establece el código de estado a 500 lo que significa que se produjo un error, y devuelve el error al usuario.

Ejecute nuestro servidor con el comando node:

  • node htmlFile.js

En el navegador web, visite http://localhost:8000. Verá esta página:

Imagen de página HTML cargada desde un archivo en Node.js

Ahora devolvió al usuario una página HTML desde el servidor. Puede salir del servidor en ejecución con CTRL+C. Verá de nuevo la solicitud del terminal cuando lo haga.

Cuando escriba un código como este en producción, es posible que no quiera cargar una página HTML cada vez que reciba una solicitud HTTP. Aunque esta página HTML tiene aproximadamente 800 bytes de tamaño, los sitios web más complejos pueden tener megabytes de tamaño. Los archivos grandes pueden tardar más en cargar. Si su sitio espera mucho tráfico, puede ser mejor cargar los archivos HTML al inicio y guardar su contenido. Tras cargarse, puede configurar el servidor y hacer que escuche las solicitudes en una dirección.

Para demostrar este método, veamos cómo podemos rediseñar nuestro servidor para que sea más eficiente y escalable.

Presentar HTML de forma eficiente

En vez de cargar el HTML para cada solicitud, en este paso lo cargaremos una sola vez al principio. La solicitud devolverá los datos que cargamos al inicio.

En el terminal, vuelva a abrir la secuencia de comandos Node.js con un editor de textos:

  • nano htmlFile.js

Vamos a comenzar añadiendo una nueva variable antes de crear la función requestListener():

first-servers/htmlFile.js
...
let indexFile;

const requestListener = function (req, res) {
...

Cuando ejecutemos este programa, esta variable tendrá el contenido del archivo HTML.

Ahora, volvamos a ajustar la función requestListener(). En vez de cargar el archivo, ahora devolverá el contenido de indexFile:

first-servers/htmlFile.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "text/html");
    res.writeHead(200);
    res.end(indexFile);
};
...

A continuación, cambiamos la lógica de lectura del archivo desde la función requestListener() al inicio de nuestro servidor. Realice los siguientes cambios mientras creamos el servidor:

first-servers/htmlFile.js
...

const server = http.createServer(requestListener);

fs.readFile(__dirname + "/index.html")
    .then(contents => {
        indexFile = contents;
        server.listen(port, host, () => {
            console.log(`Server is running on http://${host}:${port}`);
        });
    })
    .catch(err => {
        console.error(`Could not read index.html file: ${err}`);
        process.exit(1);
    });

Guarde el archivo y salga de nano con CTRL+X.

El código que lee el archivo es similar a lo que escribimos en nuestro primer intento. Sin embargo, cuando leemos correctamente el archivo, guardamos el contenido en nuestra variable indexFile global. A continuación, iniciamos el servidor con el método listen(). Lo más importante es que el archivo se cargue antes de ejecutar el servidor. De esta forma, la función requestListener() devolverá seguro una página HTML, ya que indexFile ya no es una variable vacía.

Nuestro controlador de errores ha cambiado también. Si no se puede cargar el archivo, capturamos el error y lo imprimimos en nuestra consola. A continuación, salimos del programa Node.js con la función exit() sin iniciar el servidor. De esta forma, podemos ver por qué falló la lectura del archivo, resolver el problema e iniciar el servidor de nuevo.

Ahora hemos creado diferentes servidores web que devuelven varios tipos de datos a un usuario. Hasta ahora, no hemos usado ninguna solicitud de datos para determinar qué deberíamos devolver. Deberemos usar solicitar datos cuando se configuren diferentes rutas en un servidor Node.js, así que veamos cómo funcionan juntos.

Paso 4: Administrar rutas usando un objeto de solicitud HTTP

La mayoría de los sitios web que visitamos o APIs que usamos normalmente tienen más de un endpoint, para que podemos acceder a varios recursos. Un buen ejemplo sería un sistema de administración de libros, uno que pueda usarse en una biblioteca. No solo necesitaría administrar los datos de los libros, sino también administrar los datos del autor para facilitar la creación de catálogos y las búsquedas.

Aunque los datos para libros y autores están relacionados, hay dos objetos diferentes. En estos casos, los desarrolladores de software normalmente codifican cada objeto en endpoints diferentes como una forma de indicar al usuario API con qué tipo de datos está interactuando.

Crearemos un nuevo servidor para una pequeña biblioteca, que devolverá dos tipos de datos diferentes. Si el usuario va a la dirección de nuestro servidor /books, recibirá una lista de libros en JSON. Si van a /authors, recibirán una lista de información del autor en JSON.

Hasta ahora, hemos devuelto la misma respuesta a cada solicitud que obtuvimos. Ilustremos esto rápidamente.

Vuelva a ejecutar nuestro ejemplo de respuesta JSON:

  • node json.js

En otro terminal, vamos a hacer una solicitud cURL como antes:

  • curl http://localhost:8000

Verá lo siguiente:

Output
{"message": "This is a JSON response"}

Ahora probemos otro comando curl:

  • curl http://localhost:8000/todos

Tras pulsar Enter, verá el mismo resultado:

Output
{"message": "This is a JSON response"}

No hemos creado ninguna lógica especial en nuestra función requestListener() para gestionar una solicitud cuya URL contiene /todos, de forma que Node.js devuelve el mismo mensaje JSON por defecto.

Ya que queremos crear un servidor de gestión de una biblioteca en miniatura, ahora separaremos los tipos de datos que se devuelven en base al endpoint al que el usuario accede.

Primero, salga del servidor en ejecución con CTRL+C.

Ahora, abra routes.js en su editor de texto:

  • nano routes.js

Comenzaremos almacenando nuestros datos JSON en variables antes de la función requestListener():

first-servers/routes.js
...
const books = JSON.stringify([
    { title: "The Alchemist", author: "Paulo Coelho", year: 1988 },
    { title: "The Prophet", author: "Kahlil Gibran", year: 1923 }
]);

const authors = JSON.stringify([
    { name: "Paulo Coelho", countryOfBirth: "Brazil", yearOfBirth: 1947 },
    { name: "Kahlil Gibran", countryOfBirth: "Lebanon", yearOfBirth: 1883 }
]);
...

La variable books es una cadena que contiene JSON para una variedad de objetos de libros. Cada libro tiene un título o nombre, un autor, y el año en que se publicó.

La variable authors es una cadena que contiene el JSON para diversos objetos de autor. Cada autor tiene un nombre, un país de nacimiento y su año de nacimiento.

Ahora que tenemos los datos que nuestras respuestas devolverán, vamos a comenzar a modificar la función requestListener() para devolverlas a las rutas correctas.

Primero, nos aseguraremos de que cada respuesta desde nuestro servidor tenga el encabezado Content-Type correcto:

first-servers/routes.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
}
...

Ahora, queremos devolver el JSON adecuado, dependiendo de la ruta URL que el usuario visite. Crearemos la instrucción switch en la URL de la solicitud:

first-servers/routes.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
    switch (req.url) {}
}
...

Para obtener la ruta URL desde un objeto de solicitud, debemos acceder a su propiedad url. Ahora podemos añadir casos a la instrucción switch para devolver el JSON apropiado.

La instrucción switch de JavaScrip proporciona una forma de controlar qué código se ejecuta dependiendo del valor de un objeto o expresión JavaScript (por ejemplo, el resultado de operaciones matemáticas). Si necesita una lección o un recordatorio sobre cómo usarlas, eche un vistazo a nuestra guía sobre Cómo usar la instrucción Switch en JavaScript.

Ahora, agreguemos un caso para cuando el usuario quiera obtener nuestra lista de libros:

first-servers/routes.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
    switch (req.url) {
        case "/books":
            res.writeHead(200);
            res.end(books);
            break
    }
}
...

Establecemos nuestro código de estado a 200 para indicar que la solicitud está bien y devolver el JSON que contiene la lista de nuestros libros. Ahora, vamos a añadir otro caso para nuestro autores:

first-servers/routes.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
    switch (req.url) {
        case "/books":
            res.writeHead(200);
            res.end(books);
            break
        case "/authors":
            res.writeHead(200);
            res.end(authors);
            break
    }
}
...

Como antes, el código de estado será 200, ya que la solicitud está bien. Esta vez, devolvemos el JSON que contiene la lista de nuestros autores.

Queremos devolver un error si el usuario intenta ir a cualquier otra ruta. agreguemos el caso predeterminado para hacer esto:

routes.js
...
const requestListener = function (req, res) {
    res.setHeader("Content-Type", "application/json");
    switch (req.url) {
        case "/books":
            res.writeHead(200);
            res.end(books);
            break
        case "/authors":
            res.writeHead(200);
            res.end(authors);
            break
        default:
            res.writeHead(404);
            res.end(JSON.stringify({error:"Resource not found"}));
    }
}
...

Usamos la palabra clave default en una instrucción switch para capturar todos los otros escenarios no capturados por nuestros casos previos. Establecemos el código de estado a 404 para indicar que no se encontró la URL que estaban buscando. A continuación, añadimos un objeto JSON que contiene un mensaje de error.

Probemos nuestro servidor para ver si se comporta como esperamos. En otro terminal, primero vamos a ejecutar un comando para ver si obtenemos nuestra lista de libros:

  • curl http://localhost:8000/books

Pulse Enter para ver el siguiente resultado:

Output
[{"title":"The Alchemist","author":"Paulo Coelho","year":1988},{"title":"The Prophet","author":"Kahlil Gibran","year":1923}]

Hasta ahora, todo bien. Intentemos lo mismo para /authors. Escriba el siguiente comando en el terminal:

  • curl http://localhost:8000/authors

Verá el siguiente resultado cuando el comando se complete:

Output
[{"name":"Paulo Coelho","countryOfBirth":"Brazil","yearOfBirth":1947},{"name":"Kahlil Gibran","countryOfBirth":"Lebanon","yearOfBirth":1883}]

Por último, probemos otra URL errónea para asegurarnos de que requestListener() devuelve la respuesta del error:

  • curl http://localhost:8000/notreal

Al introducir ese comando, se mostrará este mensaje:

Output
{"error":"Resource not found"}

Puede salir del servidor en ejecución con CTRL+C.

Ahora hemos creado diferentes caminos para que nuestros usuarios obtengan datos diferentes. También hemos añadido una respuesta predeterminada que devuelve un error HTTP si el usuario introduce una URL que no admitimos.

Conclusión

En este tutorial, creó una serie de servidores HTTP Node.js. Primero devolvió una respuesta textual básica. Luego, devolvió varios tipos de datos desde nuestro servidor: JSON, CSV y HTML. Desde ahí, pudo combinar la carga de archivos con las respuestas HTTP para devolver una página HTML desde un servidor al usuario, y para crear una API que usó información sobre la solicitud del usuario para determinar qué datos deberían enviarse en su respuesta.

Ahora está preparado para crear servidores web que pueden gestionar una variedad de solicitudes y respuestas. Con este conocimiento, puede crear un servidor que devuelva muchas páginas HTML al usuario en diferentes puntos finales. También podría crear su propia API.

Para obtener más información sobre los servidores web HTTP en Node.js, puede leer la documentación sobre Node.js en el módulo http. Si desea seguir aprendiendo sobre Node.js, puede volver a la página de la serie Cómo desarrollar código en Node.js.

Creative Commons License