Decimodan
September 7, 2020

Creando tu primer CRUD API con NodeJS y Firebase Cloud Functions

En este post vamos a construir nuestro primer API usando Google Firebase, las llamadas Firebase Cloud Functions y ExpressJS.

Antes de comenzar es bastante recomendable que tengas la siguiente configuración:

Puedes encontrar el código referente a este artículo aquí. El repositorio de GitHub también contiene una colección de Postman, la cual te recomiendo importar primero y usarla para probar tu proyecto, solamente cambia las url para que coincidan con las de tu proyecto.

Conocimientos Necesarios

Para empezar, quiero repasar algunos de los conceptos necesarios sobre que son las API y cómo funcionan. Esto es completamente introductorio, así que si ya está familiarizado con estos conceptos, favor de avanzar a la siguiente sección.

API significa “Interfaz de Programación para Aplicaciones” por sus siglas en inglés y se refiere al método que utilizan los sistemas informáticos para comunicarse entre sí.

Básicamente, la idea detrás de construir un API es que tu sistema pueda comunicarse con lo que sea que estés construyendo. Las API pueden incluir endpoints REST.

Hay otros servicios llamados RESTful y estos se refieren a la transferencia de estado representacional y aprovecha el protocolo HTTP para pasar datos (a través de un API). El protocolo HTTP es lo que usamos todos los días en sitios web y aplicaciones de Internet. Los servicios RESTful utilizan diferentes verbos (o métodos) HTTP para pasar datos entre diferentes sistemas. Los verbos HTTP típicos que utilizará son los siguientes:

Un último punto, mencione endpoints anteriormente… Un endpoint es solo una forma elegante de describir una dirección por la cuál llegarán las solicitudes HTTP.

Firebase

Firebase

Firebase

Firebase es una plataforma muy poderosa que se puede usar para crear aplicaciones rápidamente. Algunos de los servicios que Firebase pone a tu disposición son los siguientes:

El único requisito para usar Firebase es una cuenta de Google. (por eso se encuentra en los requisitos…)

Más adelante explicaremos como configurar el backend para un API creada en Firebase… (si ya leíste este artículo es muy similar…)

Configuración Inicial

Vamos a empezar a configurar nuestro entorno, para eso vamos a entrar a la Consola de Firebase, tendrías que ver algo similar a esto:

Creando nuestro proyecto en Firebase

Creando nuestro proyecto en Firebase

Y le damos siguiente a los demás pasos y demás hasta que veamos una pantalla como la siguiente:

Proyecto ya creando en Firebase

Proyecto ya creando en Firebase

Una vez creado nuestro proyecto, abrimos la consola de Firebase y nos vamos a la opción “Cloud Firestore” y tendremos que ver algo como esto:

Le damos click a la opción que dice “Crear base de datos” para crear nuestra base de datos inicial. Seleccionaremos “modo de prueba” para poder habilitar escrituras y lecturas. Puedes escribir tu propias reglas especificas de seguridad en tu base de datos, más información sobre esto aquí.

Creando nuestra base de datos inicial

Creando nuestra base de datos inicial

Una vez que des click en siguiente te preguntarán en que ubicación quieres iniciar tu base de datos, cualquiera funciona pero la recomendación es que escojas la más cercana geográficamente a ti. Más información de todo esto aquí.

Seleccionando la ubicación de nuestra base de datos

Seleccionando la ubicación de nuestra base de datos

Ahora ya tenemos las piezas básicas de nuestros proyecto configuradas, ahora toca escribir el código.

El código

Vamos a empezar a escribir nuestra API de libros y voy a asumir que ya tienes instalado el Firebase CLI en tu computadora. Si esto no fuera así, te recomiendo que lo instales usando estas instrucciones.

Así que vamos a la terminal que tengas en tu sistema, y vamos a crear una carpeta para guardar nuestro proyecto, en mi caso usaré una carpeta llamada “firebase-api”:

mkdir firebase-api

Entramos a la carpeta que acabamos de crear y ahora usamos el siguiente comando:

firebase init

Tendremos que ver una pantalla como la siguiente:

Configuración inicial de un proyecto Firebase

Configuración inicial de un proyecto Firebase

Aquí tendremos que seleccionar “Functions” en el menú:

Seleccionando Functions

Seleccionando Functions

Luego tenemos que seleccionar la siguiente opción:

Seleccionar Use an existing project

Seleccionar Use an existing project

Esto nos va a permitir usar el proyecto que creamos anteriormente:

Seleccionamos el proyecto anteriormente creado

Seleccionamos el proyecto anteriormente creado

Lo siguiente es elegir la opciones, para este proyecto en especifico seleccionamos lo siguiente:

Y listo, accedemos a la carpeta generada… podremos ver que se generaron los siguientes archivos:

Ahora vamos a nuestro editor (usaremos VSCode) y tendremos que ver nuestro código algo así:

Proyecto inicial en VSCode

Proyecto inicial en VSCode

Creando nuestro primer Endpoint

Firebase Functions te permite usar ExpressJS para hospedar tu API de forma serverless. Serverless es un término que significa que se ejecuta sin servidores físicos… Técnicamente si se aloja en un servidor, pero le dejamos el trabajo de la configuración a ellos. Las API’s tradicionales requieren que configure un servidor en la nube o local que pueda alojar la aplicación. Esto significa que los desarrolladores tendrían que estar a cargo de los parches y alertas del servidor. En el mundo serverless, no nos tenemos que preocupar por nada más que el código y esta es una de las cosas que hace muy bueno a Firebase.

Entonces, para usar ExpressJS con este proyecto, vamos a agregar el siguiente código al archivo index.js:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const express = require('express');
const cors = require('cors');
const app = express();

app.use(cors({ origin: true }));

app.get('/holamundo', (req, res) => {
    return res.status(200).send('Hola mundo, soy el primer endpoint');
});

exports.app = functions.https.onRequest(app);

En las primeras lineas, agregamos las dependencias necesarias para este proyecto, más a detalle hace lo siguiente:

La parte de app.get nos permite crear el enpoint “Hola Mundo”. Estamos usando express routing aquí. Para más info de como funciona esto, puedes revisar este tutorial.

Para este propósito, este app.get está creando una petición HTTP GET que te permite crear la petición req y la respuesta res. Cuando un endpoint es llamado, regresará “Hola mundo, soy el primer endpoint” junto con un código HTTP 200, que significa que todo estuvo correcto. Más información sobre los status HTTP aquí.

La última parte del código, expone nuestra aplicación express para que pueda ser acceda. Ya con todo esto inicializado, vamos a la terminal:

firebase serve

Y tenemos que utilizar la dirección que nos provee la terminal en Postman para ver lo siguiente:

Nuestro primer endpoint

Nuestro primer endpoint

Ahora que tenemos nuestro endpoint inicial, vamos a empezar a utilizar nuestra base de datos.

CRUD API usando la base de datos

Para la API que estamos creando, necesitamos crear las operaciones necesarias que son: Crear, Leer, Actualizar y Borrar (o CRUD por sus siglas en inglés).

En Firebase, tienes dos opciones para usar la base de datos. Puedes utilizar una base de datos tradicional o puedes utilizar cloud firestore. Para este tutorial, vamos a usar cloud firestore debido a que es mas fácil de utilizar y más versatil. Cloud Firestore es una base de datos NoSQL que te permite guardar tus datos en forma de documentos y colecciones. Esto es una base de datos SQL es similar al concepto de filas y columnas.

En la sección de “Configuración Inicial”, ya agregamos cloud firestore a nuestro proyecto. Ahora vamos a acceder, para poder usar el SDK de forma local, para hacer esto tendremos que hacer lo siguiente:

Vamos a “Configuración de proyecto”:

Configuración de proyecto - Firebase

Configuración de proyecto - Firebase

De ahí nos vamos a “Cuentas de servicio” y tendremos que ver una imagen como esta:

Cuentas de Servicio - Firebase

Cuentas de Servicio - Firebase

Damos click en el botón “Generar nueva clave privada” y nos descargará un archivo que deberemos poner en el directorio de nuestro proyecto, yo lo pondré en libros-api/functions y renombraré el archivo a permisos.json.

Con todo eso creado, vamos al archivo index.js de nuestro proyecto y agregamos el siguiente código:

var permisos = require("./permisos.json");
admin.initializeApp({
  credential: admin.credential.cert(permisos),
  databaseURL: "https://url-que-te-da-firestore.firebaseio.com"
});

const db = admin.firestore();

Lo que hacemos con este código es agregar el archivo de permisos y creamos una variable llamada db, que representa nuestra instancia de firestore. Con todo estos valores creados, vamos a pasar a crear nuestro primer endpoint.

Create

Para hacer un create, necesitamos utilizar una petición POST, para eso usaremos el siguiente código:

app.post('/api/libros', (req, res) => {
    (async () => {
        try {
          await db.collection('libros').doc('/' + req.body.id + '/')
              .create({titulo: req.body.titulo});
          return res.status(200).send();
        } catch (error) {
          console.log(error);
          return res.status(500).send(error);
        }
    })();
});

Este código crea el endpoint “/api/libros” que te permite hacer una petición POST. Cuando la petición POST es hecha, agrega el titulo a nuestra base de datos llamada “libros” junto con un id. Las colecciones en las bases de datos NoSQL son colecciones de documentos.

Cuando hagamos una petición desde Postman, veremos lo siguiente:

Create endpoint, consumiéndolo

Create endpoint, consumiéndolo

Y si vamos a la página de cloud firestore, veremos lo siguiente:

Nuestro dato inicial en Cloud Firestore

Nuestro dato inicial en Cloud Firestore

Como vemos, ya tenemos nuestro dato en nuestra base de datos.

Modificación intermedia

Esto lo haremos simplemente para tener una especia de salida estándar para nuestros endpoints, pondremos el siguiente código:

function salida(codigo, entrada){
    var today = new Date();
    var date = today.getFullYear()+'-'
    +(today.getMonth()+1)+'-'
    +today.getDate()+"|"
    +today.getHours() + ":" 
    + today.getMinutes() + ":" 
    + today.getSeconds();
    
    if(codigo === "200") return {
        mensaje : "Proceso terminado exitosamente",
        folio : date,
        resultado : entrada
    }

    if(codigo === "201") return {
        mensaje : "Elemento creado exitosamente",
        folio : date,
        resultado : entrada
    }

    if(codigo === "500") return {
        mensaje: "Ocurrio un detalle en el servidor",
        folio : date,
        resultado : entrada
    }

    return {
        mensaje: "Ocurrio un detalle en el servidor",
        folio : date,
        resultado : entrada
    }
}

Y modificaremos nuestro antiguo endpoint de crear para que se vea de la siguiente manera:

app.post('/api/libros', (req, res) => {
    (async () => {
        try {
          await db.collection('libros').doc('/' + req.body.id + '/')
              .create({titulo: req.body.titulo});
          return res.status(200).send(salida("200", "Libro creado correctamente"));
        } catch (error) {
          console.log(error);
          return res.status(500).send(salida("500", error));
        }
    })();
});

Lo que nos dará la siguiente salida:

Salida de nuestro nuevo endpoint

Salida de nuestro nuevo endpoint

Cómo podemos observar, con esto tenemos una salida más entendible.

Leer

Este endpoint nos va a permitir leer todo lo que tengamos en nuestra base de datos:

app.get("/api/libros", async (req, res) => {
  try{
      
      let query = db.collection("libros");
      const querySnapshot = await query.get();
      let docs = querySnapshot.docs;

      const response = docs.map((doc) => ({
          id: doc.id,
          titulo: doc.data().titulo,
      }));

      return res.status(200).json(salida("200", response));
  } catch (error) {
      return res.status(500).json(salida("500", error));
  }
})

Y este nos va a permitir leer un elemento en especifico:

app.get('/api/libros/:libro_id', (req, res) =>{
  (async () => {
      try{
          const doc = db.collection('libros').doc(req.params.libro_id);
          const item = await doc.get()
              const response = item.data();
              return res.status(200).json(salida("200", response))
      } catch(error) {
          return res.status(500).send(salida("500", error));
      }
  })();
});

Modificar

Este endpoint nos va a permitir modificar nuestros elementos:

app.put("/api/libros/:libros_id", async (req, res) => {
  try {      
      const document = db.collection("libros").doc(req.params.libros_id);
      
      await document.update({
          nombre: req.body.nombre,
      });
      
      return res.status(200).json(salida("200", "El libro ha sido actualizado"));
  } catch (error) {
    return res.status(500).json(salida("500", error));
  }
});

Borrar

app.delete("/api/libros/:libros_id", async (req, res) => {
  try {
    const doc = db.collection("libros").doc(req.params.libros_id);
    await doc.delete();
    return res.status(200).json(salida("200", "El libros ha sido borrado exitosamente"));
  } catch (error) {
    return res.status(500).send(salida("500", error));
  }
});

Subiendo nuestro API a Firebase

Ya que tenemos nuestro API CRUD funcional es el momento de subirla a Firebase. Para eso lo único que tenemos que hacer es lo siguiente:

firebase deploy

Y listo, con esto ya tenemos todo para subir nuestro API a producción, una vez que termine el proceso, obtendremos nuestra url para poder acceder a nuestra API y habremos finalizado.

Conclusiones

Y listo, como podemos ver, utilizar Firebase para nuestro backend es una solución bastante rápida de implementar y nos permite liberar rápidamente una API a producción… En el siguiente artículo veremos cómo conectar una PWA a esta API que creamos.

Saludos!!

Contáctame

O simplemente mándame un saludo 🙈