Guía Practica de Contenedores: Desarrollando una API CRUD con Node.js, Express y MySQL (1/3)
Tags: node, mysql, javascript
Published: 2021-10-04 22:13:00

*(Esta es la primera parte de una guía practica sobre contenedores que abarca el desarrollo de un API CRUD, el uso de Docker para la contenerizacion de la aplicación, la incorporación de Docker Compose para la sincronización de múltiples contenedores, y finalmente Kubernetes para la orquestación de los contenedores que conforman la aplicación que desarrollaremos.) [Codigo Fuente](https://github.com/crisemcon/Guia-Practica-Contenedores)*

En esta oportunidad desarrollaremos una API simple, incluyendo las principales operaciones CRUD (Create, Read, Update y Delete). Esta API actuara como un servicio para la gestión de estudiantes.
Primero instalaremos las tecnologías necesarias para crear nuestra aplicación:

------------------------------------------

##1) Requisitos

Primero instalaremos las tecnologías necesarias para el desarrollo de esta guia: Ubuntu, Node.js, MySQL y Postman.
*Si ya posees las tecnologias mencionadas anteriormente, puedes saltarte a la seccion 2*

##1.1) Instalando Ubuntu

Usaremos Ubuntu como sistema operativo. Si eres usuario de Windows o Mac, puedes instalar una máquina virtual con este sistema operativo. En nuestro caso, instalaremos Linux Ubuntu como nuestro sistema operativo principal. 
Para esto, descargamos la imagen ISO oficial de Ubuntu en: https://ubuntu.com/download/desktop y la grabamos en una unidad USB usando Rufus, herramienta que puede ser descargada aquí: https://rufus.ie/en_US/. 
Luego, iniciamos la unidad USB en el arranque e instalamos Ubuntu.

*Más detalles sobre este paso en: https://turbofuture.com/computers/How-to-Create-a-Bootable-USB-Ubuntu-Installer-in-Windows*

------------------------------------------


##1.2) Instalando Node.js

Si visitamos el sitio web oficial de Node.js (https://nodejs.org/), podemos ver la última versión estable, denominada LTS (Long Term Support). 
A momento de escribir esta guía, 14.16.1 es la última versión estable de Node.js. Entonces, primero, instalaremos un PPA para tener acceso a sus paquetes.
```
cd ~
curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
```

El PPA se agregará a nuestra configuración y nuestra caché de paquetes locales se actualizará automáticamente. Ahora instalamos Node.js.
```
sudo apt install nodejs
```

Podemos verificar la versión que tenemos instalada ejecutando el comando:
```
node -v
```

Además, este proceso instalará npm, que es un administrador de paquetes para Node.js. Podemos verificar la versión de npm con:
```
npm -v 
```

*Más detalles sobre este paso en: https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-20-04-es*

------------------------------------------

##1.3) Instalando MySQL

Para instalar MySQL en la máquina utilizaremos el repositorio de paquetes APT. Escribimos en la terminal los siguientes comandos:
```
sudo apt update
sudo apt install mysql-server
```

*Mas detalles sobre este paso en: https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-20-04-es*

------------------------------------------

##1.4) Instalando Postman

La forma más sencilla para instalar Postman en Ubuntu es mediante el sistema de gestión de paquetes Snappy. Los Snaps son paquetes de software autónomos que incluyen el binario de todas las dependencias necesarias para ejecutar la aplicación. Los paquetes Snap se pueden instalar desde la línea de comandos o mediante la aplicación de software de Ubuntu.
Para instalar el complemento Postman, abrimos nuestra terminal y ejecutamos el siguiente comando: 
```
sudo snap install postman
```

*Mas detalles sobre este paso en: https://linuxize.com/post/how-to-install-postman-on-ubuntu-20-04/*

******************************************

##2) Creando nuestro proyecto
El directorio de nuestro proyecto tendrá la siguiente estructura:

- studentmanagementcrud
     - package.json
     - script.js

Para lograr esto, creamos nuestro directorio de proyecto e inicializamos la configuración del proyecto: 
```
mkdir studentmanagementcrud
npm -y init
```
Esto creará nuestro archivo package.json. Ahora necesitamos instalar los paquetes necesarios para este proyecto:

express.js: Framework web.
mysql: Controlador Node.js para MySQL.
nodemon: Herramienta que permite reiniciar automáticamente el servidor cada vez que existen cambios en el código. 
```
npm i --s express express-handlebars mysql
```
Además, queremos instalar nodemon globalmente, para que pueda acceder a cualquier archivo en nuestra máquina:
```
npm i -g nodemon
```
Una vez que hayamos terminado de instalar los paquetes, el archivo package.json debería verse así: 
```json
{
  "name": "studentmanagementcrud",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "express-handlebars": "^5.3.0",
    "mysql": "^2.18.1",
  }
}
```

******************************************

##3) Creando la base de datos

Necesitamos crear una base de datos MySQL "studentmanagement" con una sola tabla llamada "student". Para ello, introducimos los siguientes comandos en la terminal: 
```
sudo mysql
```
```sql
CREATE DATABASE studentmanagement;
USE studentmanagement;
CREATE TABLE student (student_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,student_name VARCHAR(50),student_email VARCHAR(100), student_phone VARCHAR(15));
```
Además, vamos a utilizar un procedimiento almacenado (Stored Procedure) en nuestra base de datos, el cual procesara nuestras solicitudes de inserción o actualización. Ya estamos dentro de mysql en nuestra terminal, así que usamos este comando:
```sql
DELIMITER //

CREATE DEFINER=`root`@`localhost` PROCEDURE `studentAddOrEdit`(
IN _student_id INT,
IN _student_name VARCHAR(50),
IN _student_email VARCHAR(100),
IN _student_phone VARCHAR(15)
)
BEGIN
IF _student_id = 0 THEN
INSERT INTO student(student_name,student_email,student_phone)
VALUES (_student_name,_student_email,_student_phone);
ELSE
UPDATE student
SET
student_name = _student_name,
student_email = _student_email,
student_phone = _student_phone
WHERE student_id = _student_id;
END IF;
SELECT _student_id AS 'student_id';
END //
```

******************************************

##4) Creando la API

A continuación crearemos un archivo llamado script.js, el cual actuara como el punto de entrada de nuestra aplicación y contiene todos los endpoints y controladores. La ejecución de este programa invocara al servidor habilitando un puerto para el acceso de peticiones HTTP y establecerá la conexión con la base de datos creada anteriormente. 

Entonces, en el directorio de nuestro proyecto, ejecutamos el siguiente comando:
```
touch script.js
```
Este archivo debe contener el siguiente código, el cual posee comentarios para describir su funcionalidad:

```javascript
const mysql = require("mysql");
const express = require("express");

var app = express();
//Configuring express server
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

//MySQL details
var mysqlConnection = mysql.createConnection({
  host: "localhost",
  user: "root",
  password: "password",
  database: "studentmanagement",
  multipleStatements: true,
});

mysqlConnection.connect((err) => {
  if (!err) console.log("Connection Established Successfully");
  else console.log("Connection Failed!" + JSON.stringify(err, undefined, 2));
});

//Establish the server connection
//PORT ENVIRONMENT VARIABLE
const port = process.env.PORT || 8080;
app.listen(port, () => console.log(`Listening on port ${port}..`));

//Creating GET Router to fetch all the student details from the MySQL Database
app.get("/students", (req, res) => {
  mysqlConnection.query("SELECT * FROM student", (err, rows, fields) => {
    if (!err) res.send(rows);
    else console.log(err);
  });
});

//Router to GET specific student detail from the MySQL database
app.get("/students/:id", (req, res) => {
  mysqlConnection.query(
    "SELECT * FROM student WHERE student_id = ?",
    [req.params.id],
    (err, rows, fields) => {
      if (!err) res.send(rows);
      else console.log(err);
    }
  );
});

//Router to INSERT/POST a student's detail
app.post("/students", (req, res) => {
  let student = req.body;
  if(student.student_id === undefined){
    student.student_id = 0;
  }
  var sql =
    "SET @student_id = ?;SET @student_name = ?;SET @student_email = ?;SET @student_phone = ?; CALL studentAddOrEdit(@student_id,@student_name,@student_email,@student_phone);";
  mysqlConnection.query(
    sql,
    [
      student.student_id,
      student.student_name,
      student.student_email,
      student.student_phone,
    ],
    (err, rows, fields) => {
      if (!err) res.send("Student Created Successfully");
      else console.log(err);
    }
  );
});

//Router to UPDATE a student's detail
app.put("/students", (req, res) => {
  let student = req.body;
  var sql =
    "SET @student_id = ?;SET @student_name = ?;SET @student_email = ?;SET @student_phone = ?; CALL studentAddOrEdit(@student_id,@student_name,@student_email,@student_phone);";
  mysqlConnection.query(
    sql,
    [
      student.student_id,
      student.student_name,
      student.student_email,
      student.student_phone,
    ],
    (err, rows, fields) => {
      if (!err) res.send("Student Details Updated Successfully");
      else console.log(err);
    }
  );
});

//Router to DELETE a student's detail
app.delete("/students/:id", (req, res) => {
  mysqlConnection.query(
    "DELETE FROM student WHERE student_id = ?",
    [req.params.id],
    (err, rows, fields) => {
      if (!err) res.send("Student Record deleted successfully.");
      else console.log(err);
    }
  );
});

```

*Nuestro programa necesita conectarse con nuestra base de datos MySQL. Pero, para hacer esto, debemos asegurarnos de tener una contraseña adecuada en MySQL. De forma predeterminada, el usuario es "root" y la contraseña es "root". Necesitamos establecer una contraseña que coincida con la establecida en el código. En nuestro caso el usuario es "root" y la contraseña es "password". Para lograr esto, se ejecutan los siguientes comandos en la sesión de mysql:*

```sql
ALTER USER 'root'@'localhost' IDENTIFIED BY 'password';
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
FLUSH PRIVILEGES;
```

A continuacion ejecutaremos el servidor para que comience a esperar solicitudes en el puerto 8080. En el directorio del proyecto abrimos la terminal y ejecutamos:
```
nodemon script.js
```
Esto deberia mostrar el siguiente mensaje:
![nodemon script.js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fy0mqjep3aphrx4sc2ak.png)

Ahora podemos comenzar a enviar peticiones HTTP a nuestro servidor, que está escuchando en el puerto 8080. Para esto, usaremos Postman:

******************************************

##5) Probando nuestra API con Postman

En este apartado realizaremos las peticiones HTTP a nuestra API con Postman, y confirmaremos su resultado con la terminal MySQL.
Vamos a crear un estudiante con una peticion POST en la ruta http://localhost:8080/students: 
![post student postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ybwcky6tua37d364uyc.png)
![post student mysql](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9ajpvxbi2cilvumvvby.png)

Podemos obtener todos los estudiantes registrados con una peticion GET en la ruta http://localhost:8080/students: (He creado otro estudiante siguiendo el proceso descrito en el paso anterior)
![get students postman]![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ij4hk7zvi2md3sjztb6l.png)
![get students results postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7d0q2wibpgt185fwdpcc.png)

Podemos editar un alumno con una peticion PUT en la ruta http://localhost:8080/students: 
![put students postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/okclopr2wtx4gu08xi4s.png)
![put students mysql](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dfds193or9439s2lmub3.png)

Podemos obtener un estudiante específico con una peticion GET en la ruta http://localhost:8080/students/:id de la siguiente manera: 
![get specific student postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j3wqfieqihwitx7mrznv.png)
![get specific student mysql](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/07u0azodrpi7685hg2to.png)

Podemos eliminar un estudiante específico con una peticion DELETE en la ruta http://localhost:8080/students/:id de la siguiente manera: 
![delete student postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/le8i1qjj8zpsxnpq03nx.png)
![delete student mysql](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ilo5vrfdhpstgi75c4pq.png)

******************************************

## Conclusion

En esta primera parte de la guia practica de contenedores creamos un servidor local usando Node.js, Express y MySQL, y finalmente probamos la funcionalidad de nuestra API con la herramienta Postman. 
En la siguiente guia utilizaremos Docker para construir una imagen de Node.js que incluya nuestro programa y ejecutar nuestro propio contenedor. Ademas, utilizaremos Docker Compose para ejecutar en simultaneo 3 contenedores. 

Espero que te haya sido de utilidad esta guia y nos vemos en la siguiente!