Profundizando en Docker, técnicas básicas de administración
Es crucial entender algunos parámetros comunes en Docker que son esenciales para su administración a …
leer másExpress.js es un marco de trabajo minimalista, flexible y proporciona un robusto conjunto de características para aplicaciones web. Con Express, puedes construir soluciones backend eficientes que gestionan la lógica de servidor, rutas, interacciones con bases de datos y mucho más, todo bajo una arquitectura fácil de entender y utilizar.
Es ampliamente utilizado en el desarrollo web debido a su velocidad, flexibilidad y facilidad de uso. Al ser un marco de trabajo minimalista, permite a los desarrolladores crear aplicaciones backend de manera rápida y eficiente. Una de las características distintivas de Express.js es su sistema de enrutamiento sencillo y efectivo, que permite definir rutas y manejar solicitudes HTTP de forma clara y concisa.
Ofrece también una amplia gama de middlewares, que son funciones que se ejecutan durante el ciclo de vida de una solicitud HTTP. Éstos pueden utilizarse para tareas como autenticación, compresión de datos, registro de solicitudes, entre otros. La flexibilidad de Express.js para integrar middlewares de terceros facilita la implementación de funcionalidades adicionales en una aplicación sin tener que realizar una programación compleja desde cero. En este caso utilizamos un Middleware que nos permitirá interpretar el cuerpo de las solicitudes HTTP que contienen datos en formato JSON.
El protocolo HTTP, definido en el RFC 2616, especifica una serie de métodos que se utilizan para realizar operaciones en un servidor web. Los métodos más comunes son GET, POST, PUT, DELETE, y OPTIONS, entre otros. Express.js proporciona métodos para manejar cada uno de estos tipos de solicitudes HTTP, lo que permite a los desarrolladores crear aplicaciones web altamente funcionales y eficientes. Cada uno de estos métodos se utiliza para realizar una operación específica en un recurso en el servidor web.
Se trata de un estándar muy antiguo y que ha sido actualizado con el paso del tiempo y por tanto ha cambiado desde entonces pero, en esencia, sigue siendo el mismo. Los métodos de solicitud HTTP son fundamentales para el desarrollo de aplicaciones web y móviles, ya que permiten a los desarrolladores realizar operaciones en un servidor web de forma eficiente y segura.
Crear una API REST con Express es sorprendentemente sencillo, nos permite centrarnos en la lógica específica de su aplicación en lugar de los detalles de bajo nivel del manejo de solicitudes HTTP. Vamos a proceder con el ejemplo práctico de cómo desplegar una API REST con Node.js y Express utilizando Docker. Este ejemplo proporcionará a los lectores una guía paso a paso para configurar, desarrollar y desplegar nuestra propia API.
Asegúrate de tener Docker instalado en tu sistema. Sigue las instrucciones de instalación de
nuestro artículo de introducción
si aún no lo has hecho. Para empezar crea una nueva carpeta src
para tu proyecto y navega a ella, dentro crea un
archivo app.js
e inicializamos el proyecto utilizando npm
.
npm init -y
Luego utilizamos la misma herramienta para instalar Express:
npm install express
Dentro de la carpeta del proyecto, hemos creado el fichero app.js
con el código fuente de nuestra API. Lo
abriremos con nuestro editor de texto o IDE preferido y añadiremos el siguiente código para crear una API REST básica
con un simple endpoint en la ruta raíz para empezar:
const express = require('express');
const app = express();
const PORT = 3000;
app.use(express.json());
app.get('/', (req, res) => {
res.send('¡Hola Mundo con Express y Docker!');
});
app.listen(PORT, () => {
console.log(`Servidor corriendo en http://localhost:${PORT}`);
});
El código anterior crea un servidor Express que escucha en el puerto 3000 y responde con el mensaje “¡Hola Mundo con
Express y Docker!” cuando se accede a la ruta raíz utilizando el método GET del protocolo HTTP. La
línea app.use(express.json())
es un middleware necesario para que Express pueda interpretar el cuerpo de las
solicitudes HTTP que
contienen datos en formato JSON. Ahora que hemos creado nuestra API, vamos a crear un archivo Dockerfile para definir
cómo se construirá nuestra imagen de Docker. Dentro de la carpeta del proyecto, crea un archivo llamado Dockerfile
y
añade el siguiente contenido:
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Este fichero es el que indica a Docker cómo construir la imagen de nuestra aplicación. En este caso, estamos utilizando
la imagen oficial de Node.js en su versión 14 basada en Alpine Linux. Este Docker se
encarga de crear un directorio de trabajo llamado /app dónde instalará las dependencias de nuestra aplicación
con npm install
utilizando el package.json
que copiará de nuestra carpeta de trabajo. A continuación, copiará el
resto de los ficheros de la aplicación a la carpeta de trabajo y expondrá el puerto 3000 para hacerlo accesible desde el
host principal. Por último, ejecutará el comando node app.js
para iniciar el servidor Express.
Con esto ya estamos listos para Dockerizar nuestra aplicación y hacer el despliegue, en entorno local en este caso, aunque sólo bastarían unos cuántos ajustes para poder hacer el despliegue a producción. Para construir la imagen de Docker, ejecutamos el siguiente comando en la terminal:
docker build -t library-api .
Esto creará una imagen de Docker llamada library-api
basándose en el Dockerfile que hemos creado. Una vez que la
imagen se haya construido, podemos ejecutar un contenedor de Docker basado en ella con el siguiente comando:
docker run -p 3000:3000 library-api
De esta forma lanzaremos un contenedor Docker que escucha en el puerto 3000 y redirige las solicitudes a nuestra aplicación gestionando la solicitud HTTP recibida en base a lo que el código fuente determine, en este caso, devolver el mensaje “¡Hola Mundo con Express y Docker!”. Si todo ha ido bien, nada más lanzar el contenedor Docker deberíamos ver en la terminal:
Servidor corriendo en http://localhost:3000.
Podemos probar accediendo desde nuestro navegador preferido a la dirección URL indicada y deberíamos ver el mensaje de bienvenida.
Ahora vamos a crear endpoints más complejos para nuestra API, por ejemplo, gestionaremos el catálogo ficticio de una
biblioteca de libros ,para ello creamos una variable para almacenar la lista de libros con su id, título, autor,
año y disponibilidad. Esta lista, en un principio, se almacenará en la memoria del servidor, en este caso nuestro
contenedor Docker, por lo que sólo estarán disponibles mientras el contenedor esté en ejecución. Añadimos el siguiente código al
archivo app.js
:
let books = [
{id: 1, title: "El Quijote", author: "Miguel de Cervantes", year: 1605, available: true},
{id: 2, title: "Cien años de soledad", author: "Gabriel García Márquez", year: 1967, available: true}
];
Con estos datos de inicio vamos a modificar el fichero app.js
para añadir el endpoint que nos permita acceder a la
lista de libros:
app.get('/books', (req, res) => {
res.status(200).json(books);
});
Para poder aplicar los cambios y hacer uso de nuestro nuevo endpoint, tendremos que parar el contenedor Docker que está en ejecución y volver a construir la imagen y lanzarlo de nuevo. Para ello, ejecutamos por separado los siguientes comandos en la terminal:
docker stop $(docker ps -a | grep "library-api" | awk '{print $1}')
docker build -t library-api .
docker run -p 3000:3000 library-api
Estos comandos se encargan de detener el contenedor que está en ejecución por su nombre gracias a la combinación
con grep
y awk
, reconstruir nuestra imagen de Docker y lanzarla de nuevo. Ahora, tras acceder a la dirección
URL http://localhost:3000/books
, deberíamos ver la lista de libros en formato JSON:
[
{
"id": 1,
"title": "El Quijote",
"author": "Miguel de Cervantes",
"year": 1605,
"available": true
},
{
"id": 2,
"title": "Cien años de soledad",
"author": "Gabriel García Márquez",
"year": 1967,
"available": true
}
]
Para no tener que andar parando y volviendo a lanzar el contenedor cada vez que hagamos un cambio en el código fuente, vamos a agregar todos los endpoint que necesitemos y lo reiniciamos:
// Crear un nuevo libro
app.post('/book', (req, res) => {
const {title, author, year, available} = req.body;
const newBook = {id: books.length + 1, title, author, year, available};
books.push(newBook);
res.status(201).send(newBook);
});
// Crear libros en bloque
app.post('/books', (req, res) => {
const newBooks = req.body.books;
newBooks.forEach(book => {
const newBook = {
id: books.length + 1,
title: book.title,
author: book.author,
year: book.year,
available: book.available
};
books.push(newBook);
});
res.status(201).json(newBooks);
});
// Actualizar un libro
app.put('/books/:id', (req, res) => {
const {id} = req.params;
const {title, author, year, available} = req.body;
const book = books.find(book => book.id === parseInt(id));
if (book) {
book.title = title;
book.author = author;
book.year = year;
book.available = available;
res.status(200).json(book);
} else {
res.status(404).send('Libro no encontrado');
}
});
// Eliminar un libro
app.delete('/books/:id', (req, res) => {
const {id} = req.params;
const book = books.find(book => book.id === parseInt(id));
if (book) {
books = books.filter(book => book.id !== parseInt(id));
res.status(200).send('Libro eliminado');
} else {
res.status(404).send('Libro no encontrado');
}
});
Tras reiniciar el contenedor Docker, podremos probar los nuevos endpoints con un cliente HTTP como puede ser Insomnium, Postman o cualquier otro. Podremos crear, actualizar y eliminar libros de nuestra biblioteca ficticia, en nuestro caso elegimos Insomnium, un cliente HTTP de código abierto y multiplataforma que nos permite realizar peticiones HTTP a nuestra API y ver las respuestas desde nuestro equipo local sin requisitos de cuentas en la nube o servidores ajenos como intermediarios.
Podemos realizar una solicitud POST a la dirección URL http://localhost:3000/book
con el siguiente cuerpo para añadir
un nuevo libro a nuestra biblioteca:
{
"id": 3,
"title": "1984",
"author": "George Orwell",
"year": 1949,
"available": false
}
Si todo ha ido bien, deberíamos recibir una respuesta con el nuevo libro añadido similar al cuerpo de la solicitud. En la imagen a continuación podemos ver cómo hemos añadido dos nuevos libros a nuestra biblioteca ficticia utilizando Insomnium junto a su respuesta debajo:
También podemos crear una solicitud para obtener la lista de libros actualizada:
A partir de aquí ya podremos hacer uso de las demás operaciones CRUD
(Create, Read, Update, Delete)
para gestionar nuestra lista de libros. Con esto hemos creado una API REST con Node.js y Express y la hemos desplegado en
un contenedor Docker. Hemos creado endpoints para acceder a la lista de libros, añadir nuevos libros, actualizar libros existentes y
eliminar libros. Hemos utilizado Insomnium para probar nuestra API y hemos visto cómo podemos realizar operaciones CRUD
en nuestra API utilizando un cliente HTTP. Este es un ejemplo sencillo de cómo podemos crear una API REST con Node.js y
Express y desplegarla en un contenedor Docker. Espero que este artículo te haya resultado útil y te haya proporcionado
una guía paso a paso para crear tu propia API REST con Node.js
y Express.
En el próximo capítulo veremos como desplegar la misma API pero con persistencia de datos utilizando una base de datos
virtualizada con Docker y utilizando docker-compose
en vez de Docker directamente, lo que nos permitirá tener
una arquitectura más compleja y realista incluso combinando diferentes servicios en un mismo contenedor para una gestión
más eficiente de nuestros datos y proyectos.
Si tienes alguna pregunta o comentario, no dudes en dejarlo en la sección de comentarios a continuación. ¡Happy Coding!
Quizá te puedan interesar
Es crucial entender algunos parámetros comunes en Docker que son esenciales para su administración a …
leer másDocker es una plataforma de virtualización que ofrece una metodología única para empaquetar y …
leer másEn el último capítulo conocimos lo básico de Linux, las distribuciones más utilizadas y amigables …
leer másDe concepto a realidad