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) ​​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 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 *