Introducción
Contenido
En este tutorial, vamos a crear una API REST para administrar libros con Node.js y Express. Para comenzar, supongo que tiene Node.js instalado, tiene algo de experiencia en JavaScript y algunos conocimientos básicos de HTML y Bootstrap.
En aras de la simplicidad, no usaremos una base de datos, por lo que no necesita experiencia en el uso de una. En su lugar, usaremos una matriz de JavaScript simple para almacenar nuestros datos.
¿Qué es una API REST?
REpresentación State Transfer (REST) es un conjunto de métodos en los que los clientes HTTP pueden solicitar información del servidor a través del protocolo HTTP. Más adelante en este artículo profundizaremos un poco más sobre los métodos HTTP.
En una colección de datos, como libros, por ejemplo, hay algunas acciones que tendremos que realizar con frecuencia, que se reducen a: Crear, Leer, Actualizar y Eliminar (también conocido como Funcionalidad CRUD).
Las API REST utilizan diferentes métodos de solicitud HTTP, correspondientes a las acciones mencionadas anteriormente, para recuperar y manipular datos.
¿Qué es Express?
ExpressJS es una de las bibliotecas de servidor HTTP más populares para Node.js, que se envía con funcionalidades muy básicas. Sin embargo, es altamente personalizable usando algo llamado «middleware». ExpressJS es muy fácil de usar, instalar y aprender.
Aunque solo vamos a crear una API REST en este artículo, el marco de ExpressJS no se limita solo a eso: puede hacer todo tipo de cosas como alojar archivos estáticos, realizar renderizado del lado del servidor o incluso usarlo como proxy servidor.
Tipos de solicitud HTTP
Hay algunos tipos de métodos HTTP que debemos conocer antes de crear una API REST. Estos son los métodos que corresponden a las tareas CRUD:
POST
: Se utiliza para enviar datos, normalmente se utiliza para crear nuevas entidades o editar entidades ya existentesGET
: Se utiliza para solicitar datos del servidor, normalmente se utiliza para leer datosPUT
: Se usa para reemplazar completamente el recurso con el recurso enviado, generalmente se usa para actualizar datosDELETE
: Se utiliza para eliminar una entidad del servidor
Nota: Tenga en cuenta que puede utilizar POST
o PUT
para editar los datos almacenados. Eres libre de elegir si quieres usar PUT
ya que se puede omitir por completo. Sin embargo, mantenga la coherencia con los verbos HTTP que utiliza. Si estas usando POST
para crear y actualizar, entonces no use el PUT
método en absoluto.
Qué vamos a construir
Ahora tenemos una comprensión básica de qué es REST y los métodos HTTP básicos. Creemos una aplicación sencilla para almacenar información sobre libros. En esta aplicación, almacenaremos información sobre el ISBN del libro, título, autor, fecha de publicación, editorial y número de páginas.
Te puede interesar:Desplegando una aplicación Node.js en un Droplet de DigitalOcean con DockerYo usaré este conjunto de datos de GitHub para obtener algunos detalles de muestra sobre libros.
Entonces, comencemos a construir nuestra aplicación.
Configuración del proyecto
Primero, inicialicemos un nuevo proyecto de Node.js:
$ npm init
Complete la información solicitada a sus requerimientos.
Ahora podemos instalar el marco web ejecutando:
$ npm install --save express
Creación de un punto final simple
Ahora, comencemos a crear una aplicación simple «Hola mundo». Cuando visitemos nuestra aplicación en el navegador, seremos recibidos con un simple mensaje.
Primero, cree un archivo llamado hello-world.js
e importar el marco Express:
const express = require('express');
Ahora, creemos la aplicación Express:
const app = express();
Y configura nuestro puerto:
const port = 3000;
Ahora, podemos crear un sencillo GET
punto final. Cuando un usuario llega al punto final, aparecerá el mensaje «Hola mundo, desde express». Nos gustaría configurarlo para que esté en la página de inicio, por lo que la URL del punto final es /
:
app.get("https://Pharos.sh.com/", (req, res) => {
res.send('Hello World, from express');
});
En este punto, comencemos con nuestros clientes:
Te puede interesar:Leer y escribir YAML en un archivo en Node.js / JavaScriptapp.listen(port, () => console.log(`Hello world app listening on port ${port}!`))
Ejecutemos la aplicación y visitemos el único punto final que tenemos a través de nuestro navegador:
$ node hello-world.js
Hello world app listening on port 3000!
Middleware expreso
Como se mencionó anteriormente, ExpressJS es un servidor HTTP simple y no viene con muchas funciones listas para usar. El middleware actúa casi como extensiones para el servidor Express y proporciona funcionalidades adicionales en el «medio» de una solicitud. Hay extensiones de terceros como Morgan para la tala, multer para manejar la carga de archivos, etc.
Por ahora, para empezar, necesitamos instalar un middleware llamado analizador corporal, que nos ayuda a decodificar el cuerpo de una solicitud HTTP:
$ npm install --save body-parser
Dado que estamos llamando a la API desde diferentes ubicaciones pulsando puntos finales en el navegador. También tenemos que instalar el middleware CORS.
Si aún no está familiarizado con intercambio de recursos de origen cruzado, está bien por ahora. Instalemos el middleware y configurémoslo:
$ npm install --save cors
Construyendo la API
Agregar libros
Ahora podemos comenzar a construir nuestra aplicación. Crea un nuevo archivo llamado book-api.js
:
const express = require('express')
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
const port = 3000;
// Where we will keep books
let books = [];
app.use(cors());
// Configuring body parser middleware
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.post('/book', (req, res) => {
// We will be coding here
});
app.listen(port, () => console.log(`Hello world app listening on port ${port}!`));
Como puede ver, podemos configurar body-parser
importándolo y pasándolo al app.use
método, que lo habilita como middleware para Express app
ejemplo.
Estaremos usando el books
array para almacenar nuestra colección de libros, simulando una base de datos.
Hay algunos tipos de cuerpos de solicitud HTTP. Para un ejemplo, application/x-www-form-urlencoded
es el tipo de cuerpo predeterminado para formularios, mientras que application/json
es algo que usaríamos al solicitar un recurso usando jQuery o el cliente REST de backend.
Que body-parser
El middleware estará tomando el cuerpo HTTP, decodificando la información y añadiéndola al req.body
. A partir de ahí, podemos recuperar fácilmente la información del formulario, en nuestro caso, la información de un libro.
Dentro de app.post
método agreguemos el libro a la matriz de libros:
app.post('/book', (req, res) => {
const book = req.body;
// Output the book to the console for debugging
console.log(book);
books.push(book);
res.send('Book is added to the database');
});
Ahora, creemos un formulario HTML simple con los campos: ISBN, título, autor, fecha de publicación, editor y número de páginas en un archivo nuevo, digamos new-book.html
.
Enviaremos los datos a la API usando este formulario HTML action
atributo:
<div class="container">
<hr>
<h1>Create New Book</h1>
<hr>
<form action="http://localhost:3000/book" method="POST">
<div class="form-group">
<label for="ISBN">ISBN</label>
<input class="form-control" name="isbn">
</div>
<div class="form-group">
<label for="Title">Title</label>
<input class="form-control" name="title">
</div>
<!--Other fields-->
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
Aquí, nuestro <form>
El atributo de la etiqueta corresponde a nuestro punto final y la información que enviamos con el submit
El botón es la información que nuestro método analiza y agrega a la matriz. Tenga en cuenta que el method
el parámetro es POST
, al igual que en nuestra API.
Debería ver algo así al abrir la página:
Al hacer clic en «Enviar», somos recibidos con nuestras aplicaciones. console.log(book)
declaración:
{ isbn: '9781593275846',
title: 'Eloquent JavaScript, Second Edition',
author: 'Marijn Haverbeke',
publish_date: '2014-12-14',
publisher: 'No Starch Press',
numOfPages: '472' }
Nota: Tenga en cuenta que, dado que estamos usando una matriz para almacenar datos, los perderemos en el próximo reinicio de la aplicación.
Obtener todos los libros
Ahora creemos un punto final para obtener todos los libros de la API:
app.get('/books', (req, res) => {
res.json(books);
});
Reinicie el servidor. Si el servidor ya está funcionando, presione Ctrl + C
para detenerlo primero. Agrega algunos libros y abre http://localhost:3000/books
en su navegador. Debería ver una respuesta JSON con todos los libros que ha agregado.
Ahora creemos una página HTML para mostrar estos libros de una manera fácil de usar.
Esta vez, crearemos dos archivos: book-list.html
que usaremos como plantilla y book-list.js
archivo que contendrá la lógica para actualizar / eliminar libros y mostrarlos en la página:
Comencemos con la plantilla:
Te puede interesar:Introducción a las transmisiones de Node.js<div class="container">
<hr>
<h1>List of books</h1>
<hr>
<div>
<div class="row" id="books">
</div>
</div>
</div>
<div id="editBookModal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Book</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form id="editForm" method="POST">
<div class="form-group">
<label for="ISBN">ISBN</label>
<input class="form-control" name="isbn" id="isbn">
</div>
<div class="form-group">
<label for="Title">Title</label>
<input class="form-control" name="title" id="title">
</div>
<!--Other fields-->
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</div>
<!--Our JS file-->
<script src="book-list.js"></script>
Con la plantilla lista, podemos implementar la lógica real para recuperar todos los libros usando JavaScript del lado del navegador y nuestra API REST:
const setEditModal = (isbn) => {
// We will implement this later
}
const deleteBook = (isbn) => {
// We will implement this later
}
const loadBooks = () => {
const xhttp = new XMLHttpRequest();
xhttp.open("GET", "http://localhost:3000/books", false);
xhttp.send();
const books = JSON.parse(xhttp.responseText);
for (let book of books) {
const x = `
<div class="col-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">${book.title}</h5>
<h6 class="card-subtitle mb-2 text-muted">${book.isbn}</h6>
<div>Author: ${book.author}</div>
<div>Publisher: ${book.publisher}</div>
<div>Number Of Pages: ${book.numOfPages}</div>
<hr>
<button type="button" class="btn btn-danger">Delete</button>
<button types="button" class="btn btn-primary" data-toggle="modal"
data-target="#editBookModal" onClick="setEditModal(${book.isbn})">
Edit
</button>
</div>
</div>
</div>
`
document.getElementById('books').innerHTML = document.getElementById('books').innerHTML + x;
}
}
loadBooks();
En el script anterior, estamos enviando un GET
solicitud al punto final http://localhost:3000/books
para recuperar los libros y luego crear una tarjeta Bootstrap para que cada libro lo muestre. Si todo funciona correctamente, debería ver algo como esto en su página:
Probablemente haya notado los botones Editar y Crear y sus respectivos métodos. Por ahora, dejémoslos vacíos e implementémoslos a medida que avanzamos.
Recuperar un libro por ISBN
Si queremos mostrar un libro específico al usuario, necesitaremos una forma de recuperarlo de la base de datos (o la matriz, en nuestro caso). Esto siempre se realiza mediante una clave específica de esa entidad. En la mayoría de los casos, cada entidad tiene un único id
que nos ayuda a identificarlos.
En nuestro caso, cada libro tiene un ISBN único por naturaleza, por lo que no es necesario otro id
valor.
Por lo general, esto se hace analizando el parámetro de URL para un id
y buscando el libro con el correspondiente id
.
Por ejemplo, si el ISBN es 9781593275846
la URL se vería así, http://localhost:3000/book/9781593275846
:
app.get('/book/:isbn', (req, res) => {
// Reading isbn from the URL
const isbn = req.params.isbn;
});
Aquí, se nos presentan las URL parametrizadas. Dado que el ISBN depende del libro, hay potencialmente un número infinito de puntos finales aquí. Añadiendo dos puntos (:
) a la ruta, podemos definir una variable, mapeada a la variable isbn
. Entonces, si un usuario visita localhost:3000/book/5
la isbn
el parámetro será 5
.
Puede aceptar más de un parámetro en su URL si tiene sentido en su escenario. Por ejemplo /image/:width/:height
, y luego puede obtener esos parámetros usando req.params.width
y req.params.height
.
Ahora, usando nuestro punto final, podemos recuperar un solo libro:
app.get('/book/:isbn', (req, res) => {
// Reading isbn from the URL
const isbn = req.params.isbn;
// Searching books for the isbn
for (let book of books) {
if (book.isbn === isbn) {
res.json(book);
return;
}
}
// Sending 404 when not found something is a good practice
res.status(404).send('Book not found');
});
Reinicie nuevamente el servidor, agregue un libro nuevo y abra localhost/3000/{your_isbn}
y la aplicación devolverá la información del libro.
Eliminar libros
Al eliminar entidades, normalmente las eliminamos una por una para evitar una gran pérdida accidental de datos. Para eliminar elementos, usamos HTTP DELETE
y especifique un libro usando su número ISBN, tal como lo recuperamos:
app.delete('/book/:isbn', (req, res) => {
// Reading isbn from the URL
const isbn = req.params.isbn;
// Remove item from the books array
books = books.filter(i => {
if (i.isbn !== isbn) {
return true;
}
return false;
});
res.send('Book is deleted');
});
Estamos usando el app.delete
método para aceptar DELETE
peticiones. También hemos utilizado la matriz filter
método para filtrar el libro con el ISBN relevante para eliminarlo de la matriz.
Ahora implementemos el deleteBook
método en el book-list.js
archivo:
const deleteBook = (isbn) => {
const xhttp = new XMLHttpRequest();
xhttp.open("DELETE", `http://localhost:3000/book/${isbn}`, false);
xhttp.send();
// Reloading the page
location.reload();
}
En este método, estamos enviando la solicitud de eliminación cuando se presiona el botón y volviendo a cargar la página para mostrar los cambios.
Edición de libros
Muy similar a eliminar entidades, actualizarlas requiere que tomemos una específica, basada en el ISBN y luego enviemos un POST
o PUT
Llamada HTTP con la nueva información.
Volvamos a nuestro book-api.js
archivo:
app.post('/book/:isbn', (req, res) => {
// Reading isbn from the URL
const isbn = req.params.isbn;
const newBook = req.body;
// Remove item from the books array
for (let i = 0; i < books.length; i++) {
let book = books[i]
if (book.isbn === isbn) {
books[i] = newBook;
}
}
res.send('Book is edited');
});
Al enviar un POST
solicitud, dirigida a un ISBN específico, se actualiza el libro adecuado con nueva información.
Como ya hemos creado el modal de edición, podemos usar el setEditModal
método para recopilar información sobre el libro cuando se hace clic en el botón «Editar».
También configuraremos el formulario action
parámetro con la URL del libro en el que se hizo clic para enviar la solicitud:
const setEditModal = (isbn) => {
// Get information about the book using isbn
const xhttp = new XMLHttpRequest();
xhttp.open("GET", `http://localhost:3000/book/${isbn}`, false);
xhttp.send();
const book = JSON.parse(xhttp.responseText);
const {
title,
author,
publisher,
publish_date,
numOfPages
} = book;
// Filling information about the book in the form inside the modal
document.getElementById('isbn').value = isbn;
document.getElementById('title').value = title;
document.getElementById('author').value = author;
document.getElementById('publisher').value = publisher;
document.getElementById('publish_date').value = publish_date;
document.getElementById('numOfPages').value = numOfPages;
// Setting up the action url for the book
document.getElementById('editForm').action = `http://localhost:3000/book/${isbn}`;
}
Para verificar si la función de actualización funciona, edite un libro. El formulario debe llenarse con la información existente sobre el libro. Cambie algo y haga clic en «Enviar», después de lo cual debería ver el mensaje «El libro está editado».
Conclusión
Así de fácil es crear una API REST utilizando Node.js y Express. Puedes visitar el oficial Documentación exprés para obtener más información sobre el marco si está interesado.
Te puede interesar:Uso de stubs para pruebas en JavaScript con Sinon.jsAdemás, el código que he proporcionado es solo para el tutorial, nunca debe usarlo en un entorno de producción. Asegúrese de validar los datos y seguir las mejores prácticas al escribir código para producción.
Como es habitual, el código fuente de este proyecto se puede encontrar en GitHub.
.