Table of Contents

Desarrollo de aplicaciones: backend + frontend

frontendbackend.jpg
Figure 1: Frontend vs Backend (Fuente: https://ecdisis.com/que-es-frontend-y-backend/)

Configuración del proyecto

Crear un fichero package.json:

{
  "name": "cities-backend",
  "version": "0.1",
  "description": "A sample project to learn Node.js",
  "type": "module",
  "scripts": {
    "start": "node src/app.js"
  },
  "author": "Santiago Faci",
  "license": "GPL-2.0-only",
 
  "dependencies": {
    "express": "^4.21.1"
  }
}

Algunos comandos útiles:

Gestión de dependencias

Añadir las dependencias necesarias para el proyecto al fichero package.json en la sección dependencies (se pueden ir añadiendo a medida que son necesarias durante el desarrollo del proyecto):

. . .
"dependencies": {
    "express": "^4.21.1"
  }
. . .

Instalar todas las dependencias necesarias para el proyecto:

npm install

Actualizar las dependencias necesarias para el proyecto, si es necesario:

npm update

También se pueden instalar una dependencia concreta y ésta será añadida automáticamente al fichero package.json como tal:

npm install express

Backend

¿Qué es una API?

Figure 2: Web API (Fuente: https://www.programmingwithmukesh.com/articles/web-api)
Figure 3: Tipos de operaciones en una Web API

API sencilla sólo lectura

import express from 'express';
 
const app = express();
app.use(express.json());
 
const cities = {
    'Zaragoza': {
        altitude: 199,
        population: 673010
    },
    'Huesca': {
        altitude: 488,
        population: 53305
    },
    'Teruel': {
        altitude: 915,
        population: 25900
    }
};
 
app.get('/cities', (req, res) => {
    res.json(cities);
});
 
app.get('/city/:city', (req, res) => {
    const city = req.params.city;
    res.json(cities[city]);
});
 
 
app.listen(8080, () => {
    console.log('Iniciando el backend en el puerto 8080');
});

Asumiendo que tenemos un fichero package.json como el explicado anteriormente, podremos ejecutar la API con el siguiente comando:

npm start

Y tendremos los siguiente endpoints disponibles:

Figure 4: Listado de todas las ciudades
Figure 5: Detalles de una ciudad concreta

También podemos utilizar aplicaciones como Hoppscotch que son específicas para desarrolladores. Son clientes diseñados para realizar llamadas a APIs y testear que éstas funcionan correctamente.

Figure 6: Hoppscotch

Frontend

{
    "name": "cities-frontend",
    "version": "0.1",
    "source": "src/index.html",
    "type": "module",
    "scripts": {
        "start": "parcel --no-cache",
        "build": "parcel build"
    },
    "devDependencies": {
        "buffer": "^6.0.3",
        "parcel": "latest",
        "process": "^0.11.10"
    },
    "dependencies": {
        "axios": "^1.7.7"
    }
}
<!DOCTYPE html>
<html>
    <head>
        <title>Ejemplo 1: API con Node.js</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
        <script type="module" src="app.js"></script>
    </head>
    <body onload="readCities()">
        <div class="container">
            <h1>Ejemplo de API con Node.js</h1>
            <img src="nodejs.png"/>
            <ul id="cities" class="list-group">
            </ul>
        </div>
    </body>
</html>
import axios from 'axios';
 
function addCityNode(name) {
    const citiesUl = document.getElementById('cities');
 
    const item = document.createElement('li');
    item.className = 'list-group-item';
    item.appendChild(document.createTextNode(name));
    citiesUl.appendChild(item);
};
 
window.readCities = function() {
    axios.get('http://localhost:8080/cities')
        .then((response) => {
            const cityList = response.data;
 
            Object.keys(cityList).forEach(cityName => {
                addCityNode(cityName);
            });
        });
};

Tal y como hacemos con el backend, podremos ejecutar este frontend con el siguiente comando:

npm start

Hay que tener en cuenta que backend y frontend debes escuchar en puertos diferentes para poder ejecutarse al mismo tiempo. En nuestro caso el backend escucha en el puerto 8080 y el frontend en el 1234

Figure 7: cities-frontend

CRUD Backend + Frontend

Una vez que hemos visto una sencilla API con unicamente un par de operaciones de lectura, vamos a ver un ejemplo algo más completo. Es lo que también se conoce como CRUD que es el acrónimo que se utiliza para referirse a las 4 operaciones básica que permite que los usuarios puedan registrar (Create), leer (Read), modificar (Update) y borrar (Delete) información utilizando algún tipo de software.

En el caso de la API (backend) deberán ser diseñadas conforme a las convenciones que vimos un poco antes cuando explicamos lo que era una API.

Backend

import express from 'express';
import cors from 'cors';
 
const app = express();
app.use(cors());
app.use(express.json());
 
const cities = {
    'Zaragoza': {
        altitude: 199,
        population: 673010
    },
    'Huesca': {
        altitude: 488,
        population: 53305
    },
    'Teruel': {
        altitude: 915,
        population: 25900
    }
};
 
app.get('/cities', (req, res) => {
    res.json(cities);
});
 
app.get('/city/:city', (req, res) => {
    const city = req.params.city;
    res.json(cities[city]);
});
 
app.post('/cities', (req, res) => {
    const name = req.body.name;
    const altitudeValue = req.body.altitude;
    const populationValue = req.body.population;
 
    cities[name] = {
        altitude: altitudeValue,
        population: populationValue
    };
    console.log(cities);
    res.status(201).end();
});
 
app.listen(8080, () => {
    console.log('Iniciando el backend en el puerto 8080');
});

Frontend

<!DOCTYPE html>
<html>
    <head>
        <title>Ejemplo 1: API con Node.js</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
        <script type="module" src="app.js"></script>
    </head>
    <body onload="readCities()">
        <div class="container">
            <h1>Ejemplo de API con Node.js</h1>
            <img src="nodejs.png"/>
            <ul id="cities" class="list-group">
            </ul>
            <hr/>
            <form>
                <div class="mb-3">
                    <label for="name" class="form-label">Nombre</label>
                    <input class="form-control" type="text" id="name"/>
                </div>
                <div class="mb-3">
                    <label for="altitude" class="form-label">Altitud</label>
                    <input class="form-control" type="text" id="altitude"/>
                </div>
                <div class="mb-3">
                    <label for="population" class="form-label">Población</label>
                    <input class="form-control" type="text" id="population"/>
                </div>
                <button type="button" class="btn btn-primary" onClick="addCity()">Add city</button>
            </form>
        </div>
    </body>
</html>
import axios from 'axios';
 
function addCityNode(name) {
    const citiesUl = document.getElementById('cities');
 
    const item = document.createElement('li');
    item.className = 'list-group-item';
    item.appendChild(document.createTextNode(name));
 
    const button = document.createElement('button');
    button.className = 'btn-close'
    button.onclick = function() {
        removeCity(name);
        item.remove();
    };
    item.appendChild(button);
 
    citiesUl.appendChild(item);
};
 
window.readCities = function() {
    axios.get('http://localhost:8080/cities')
        .then((response) => {
            const cityList = response.data;
 
            Object.keys(cityList).forEach(cityName => {
                addCityNode(cityName);
            });
        });
};
 
window.addCity = function() {
    const name = document.getElementById('name').value;
    const altitude = document.getElementById('altitude').value;
    const population = document.getElementById('population').value;
 
    if (name === '') {
        alert('El nombre es un campo obligatorio');
        return;
    }
 
    axios.post('http://localhost:8080/cities', {
        name: name,
        altitude: altitude,
        population: population
    }).then(() => {
        addCityNode(name);
    });
};
 
window.removeCity = function(name) {
    console.log(name + ' was removed');
    // TODO Remove the city
};
Figure 8: cities-frontend-crud

Proyectos de ejemplo

Todos los proyectos de ejemplo de esta parte están en el repositorio nodejs de GitHub.


© 2024 Santiago Faci