Construyendo una API REST con Node y Express

     

    Introducci贸n

    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) 鈥嬧媏s 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 existentes
    • GET: Se utiliza para solicitar datos del servidor, normalmente se utiliza para leer datos
    • PUT: Se usa para reemplazar completamente el recurso con el recurso enviado, generalmente se usa para actualizar datos
    • DELETE: 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.

    Yo 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:

    app.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:

    <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">&times;</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.

    Adem谩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.

    .

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *