Desarrollando una sencilla API REST con NodeJS y Express

Aprende cómo desarrollar una API REST con NodeJS y Express de forma fácil y rápida, además, aprende de los múltiples métodos HTTP y empieza a hacer tus propias APIs.

Actualmente aún hay muchas empresas que siguen utilizando el clásico WebService viejo con XML (te estoy viendo a ti, equipo de TI de Estafeta) con el que tienes que entender cómo demonios mandarle los datos y cómo demonios responde y es demasiado complicado para hacer una solicitud sencilla y las API REST están aquí para salvarnos de esa complejidad ya que gracias al envío de datos por medio de JSON o su recepción por este medio podemos hacer desarrollo más rápido.

Hoy en día las API REST están en un gran apogeo ya que la mayoría hace uso de esta tecnología para poder como menciono, enviar y recibir datos de forma sencilla y rápida, aunque una tecnología que está en crecimiento es el GraphQL que busca reemplazar a las API REST para hacerlas aún más sencillas y a la vez más robustas.

Las API REST aprovechan los métodos HTTP, desde un simple POST o GET hasta métodos personalizados, sin embargo, nosotros veremos únicamente POST, GET, PUT y DELETE en su forma más sencilla y las Headers que son para autenticación, decirle qué tipo de dato va, etc. Pero esto no lo veremos aquí.

Asimismo, utilizaremos a Express para ser nuestro servidor HTTP, una recomendación es que, las API REST siempre estén detrás de Nginx ya que en caso de falla, Nginx puede seguir respondiendo, además que, permite una mejor manipulación de datos, protección de enlaces y demás cosas pero tampoco veremos esto en este tutorial, solo quise mencionarlo.

Bueno, si ya tenemos NodeJS y NPM instalados, entonces manos a la obra y si no, https://nodejs.org/es/download/ aquí pueden descargar el paquete, el proceso de instalación como de costumbre no lo voy a explicar.

Inicializando una nueva app

Para inicializar nuestra primer API REST hay 3 modos, creando el package.json a mano (no recomendado), haciendo uso del comando npm init (de cierta forma el más recomendado) o usando la dependencia de express-generator pero en esta ocasión haremos uso del npm init para ello, nos ponemos en la consola de comandos (Linux, Windows o Mac, en mi caso Windows) y navegamos al directorio donde queremos que esté nuestra API REST, en mi caso, mi carpeta de Documentos en una carpeta llamada, api . Dentro de esta carpeta corremos el comando previamente mencionado y nos irán apareciendo una serie de preguntas:

Primero nos pedirá el nombre del paquete (en si, es el nombre de nuestro proyecto), aquí ponemos api-rest

Después nos pedirá la versión del proyecto, pondremos 1.0.0 (cabe mencionar que en estos casos podemos dejar las defaults dando enter nada más)

En descripción pondremos “Mi primer API REST”.

Luego en la parte de entry point nos pide cómo se llamará nuestro archivo de entrada, el principal digamos. Dejaremos index.js tal cual.

Después nos pide un comando de prueba, un repositorio de git, palabras clave que dejaremos vacías, solo damos enter. La parte de autor, ponemos nuestro nombre si gustan.

Por último nos pide una licencia, pondremos MIT (aunque también se puede dejar la que está o poner Private) si lo desean, no es relevante a menos que vayan a liberar su API REST por alguna razón.

Una vez que tenemos esto, nos dice, ¿Está todo correcto? y le decimos que si dando enter.

Si ya quedó, entonces se generará en nuestra carpeta nuestro archivo package.json y si es así, entonces sigue instalar Express, para ello corremos el siguiente comando:

npm install express --save

Con esto, la dependencia de express se instala y se guarda en nuestro package y estamos listos para empezar.

Empezando con nuestra API

Como había mencionado previamente a la hora de inicializar nuestro proyecto, decidimos que nuestro archivo de entrada se llamara index.js y es por ello que entonces crearemos este archivo en la carpeta raíz ( .../Documents/api/index.js ) y dentro le pondremos el siguiente código:

const express = require("express");
const app = express();
app.listen(3000, () => {
 console.log("El servidor está inicializado en el puerto 3000");
});

La primer línea llama a express y la asigna, después, nuestra segunda línea la reasigna a app y así, tenemos el objeto de express para llamar listen en nuestra cuarta línea y ahí, le pasamos los parámetros para que inicialice el servidor.

Vamos a ejecutar el comando node index.js y ¡Éxito!, tenemos nuestro primer servidor, el cual, ya es accesible desde localhost:3000 pero, al abrir esto en nuestro navegador nos muestra un error, ¿qué pasó?.

Bueno, en este caso hemos iniciado un servidor pero Express no tiene ninguna declaración, es decir, ninguna ruta por lo que no sabe a dónde ir. Vamos a agregar la siguiente línea arriba de app.listen(...) :

app.get('/', function (req, res) {
res.send('Saludos desde express');
});

Detenemos nuestro servidor con CTRL+C (recordemos que yo estoy usando Windows por lo que este tipo de cosas será orientado a este SO) y después lo volvemos a iniciar con node index.js , actualizamos la página en el navegador y, ¡vualá!, tenemos una página de inicio que nos muestra Saludos desde express.

¿Qué pasó aquí?, bueno, con .get() le dimos que la ruta raíz / será la que reciba de entrada nuestras peticiones por el método GET.

Veamos rápido los métodos:

GET es el método que permite obtener un recurso desde la URL. Es decir, si yo tengo http://localhost:3000/hola el recurso solicitado es /hola si en express modificamos el primer parámetro por /hola y vamos a la dirección previamente mencionada veremos el saludo, pero si vamos a la raíz de nuevo, veremos el mismo error de hace un momento.

POST es un método que permite el envío de información del cliente hacia nuestro servidor, y puede combinarse (por así decirlo) con una parte de GET ya que, POST también puede saber la URL en la cual estará recibiendo este método. Si nosotros cambiamos .get(...) por .post(...) y vamos a la misma URL de /hola veremos un error, pero, si hacemos uso de una herramienta tercera y le mandamos un POST (como ARC) veremos “Saludos desde express”:

Ahora, si duplicamos el código y lo ponemos en ambos casos, con GET y POST y visitamos el link, podemos ver que funcionan ambos casos, pero modifiquemos un poco el código quedando algo como esto:

const express = require("express");
const app = express();

app.get('/hola', function (req, res) {
  res.send('[GET]Saludos desde express');
});

app.post('/hola', function (req, res) {
  res.send('[POST]Saludos desde express');
});

app.listen(3000, () => {
 console.log("El servidor está inicializado en el puerto 3000");
});

Y como sabemos, necesitamos reiniciar nuestro servidor cada vez que hagamos cambios, esto se puede evitar con Nodemon o PM2 pero no los veremos haremos todo una y otra vez por ahora.

Si visitamos http://localhost:3000/hola veremos [GET]Saludos desde express y si le mandamos un POST a la misma dirección veremos [POST]Saludos desde express

Así es como manejamos, GET y POST. A su vez como mencioné previamente está PUT, este método es idéntico a POST y DELETE es idéntico a GET.

¿Pero en qué se diferencian?, vamos a hacerlo de la siguiente forma:

  • GET = Obtener
  • POST = Crear
  • PUT = Actualizar
  • DELETE = Eliminar

Entonces, entendiendo esto, podemos reutilizar la misma URL con distintos métodos, por ejemplo pensando que tenemos una libreta de direcciones podemos usar la URL /usuario para obtener un usuario en GET, POST para crearlo, PUT para actualizarlo y DELETE Para eliminarlo.

Vamos a crear un objeto global entonces aprovechando esta idea, con la siguiente información:

let usuario = {
 nombre:'',
 apellido: ''
};

Y modificaremos el código base para tener las 4 opciones disponibles, GET, POST, PUT, DELETE con su respectiva dirección en /usuario plus, dos direcciones, una para el error 404 de una ruta no encontrada y, una para la raíz / .

Por último agregaremos una variable de datos que nos permitirá enviar la respuesta del servidor al cliente que nos consulta esa información.

Voy a poner primero todo el código y después iremos desmenuzando por partes, pero antes de ponerlo, necesitaremos una dependencia adicional para el manejo de datos en la recepción (es decir, manejar más fácil los datos de POST a nuestro servidor), para ello instalamos con:

npm install body-parser --save

Una vez hecho esto ahora sí, al código:

const express = require("express");
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
let usuario = {
 nombre:'',
 apellido: ''
};
let respuesta = {
 error: false,
 codigo: 200,
 mensaje: ''
};
app.get('/', function(req, res) {
 respuesta = {
  error: true,
  codigo: 200,
  mensaje: 'Punto de inicio'
 };
 res.send(respuesta);
});
app.get('/usuario', function (req, res) {
 respuesta = {
  error: false,
  codigo: 200,
  mensaje: ''
 };
 if(usuario.nombre === '' || usuario.apellido === '') {
  respuesta = {
   error: true,
   codigo: 501,
   mensaje: 'El usuario no ha sido creado'
  };
 } else {
  respuesta = {
   error: false,
   codigo: 200,
   mensaje: 'respuesta del usuario',
   respuesta: usuario
  };
 }
 res.send(respuesta);
});
app.post('/usuario', function (req, res) {
 if(!req.body.nombre || !req.body.apellido) {
  respuesta = {
   error: true,
   codigo: 502,
   mensaje: 'El campo nombre y apellido son requeridos'
  };
 } else {
  if(usuario.nombre !== '' || usuario.apellido !== '') {
   respuesta = {
    error: true,
    codigo: 503,
    mensaje: 'El usuario ya fue creado previamente'
   };
  } else {
   usuario = {
    nombre: req.body.nombre,
    apellido: req.body.apellido
   };
   respuesta = {
    error: false,
    codigo: 200,
    mensaje: 'Usuario creado',
    respuesta: usuario
   };
  }
 }
 
 res.send(respuesta);
});
app.put('/usuario', function (req, res) {
 if(!req.body.nombre || !req.body.apellido) {
  respuesta = {
   error: true,
   codigo: 502,
   mensaje: 'El campo nombre y apellido son requeridos'
  };
 } else {
  if(usuario.nombre === '' || usuario.apellido === '') {
   respuesta = {
    error: true,
    codigo: 501,
    mensaje: 'El usuario no ha sido creado'
   };
  } else {
   usuario = {
    nombre: req.body.nombre,
    apellido: req.body.apellido
   };
   respuesta = {
    error: false,
    codigo: 200,
    mensaje: 'Usuario actualizado',
    respuesta: usuario
   };
  }
 }
 
 res.send(respuesta);
});
app.delete('/usuario', function (req, res) {
 if(usuario.nombre === '' || usuario.apellido === '') {
  respuesta = {
   error: true,
   codigo: 501,
   mensaje: 'El usuario no ha sido creado'
  };
 } else {
  respuesta = {
   error: false,
   codigo: 200,
   mensaje: 'Usuario eliminado'
  };
  usuario = { 
   nombre: '', 
   apellido: '' 
  };
 }
 res.send(respuesta);
});
app.use(function(req, res, next) {
 respuesta = {
  error: true, 
  codigo: 404, 
  mensaje: 'URL no encontrada'
 };
 res.status(404).send(respuesta);
});
app.listen(3000, () => {
 console.log("El servidor está inicializado en el puerto 3000");
});

Lo primero, nuevamente, es cargar express, pero ahora también cargamos el body-parser e inicializar express en nuestra constante app . Posterior, gracias a body-parser podremos manejar los datos en formato JSON de la recepción en POST/PUT desde el cliente, es decir, que, cuando el cliente haga una petición, indicará la cabecera Content-Type: application/json y nuestra API va a servir esta petición ejecutando lo que el cliente necesita.

Después, creamos la variable usuario para tener sus propiedades previamente asignadas de forma global, así como una variable llamada respuestaque nos permitirá enviar una respuesta hacia el cliente.

Posterior creamos una ruta raíz / que nos permitirá poder mostrar una base de nuestra API.

Por último el caso del error 404 que previamente comentaba.

Y ahora sí, nuestro primer método, en GET vamos a revisar, ¿el cliente ya se creó?, si no se ha creado, entonces asignamos el error a nuestra variable de respuesta, en otro caso, mandamos el usuario en la respuesta y le indicamos al cliente que todo bien.

Caso para cuando el usuario no se ha creado
Caso para cuando el usuario ya se creó

Después en POST revisamos si, el cuerpo del POST incluye el nombre y el apellido, si no los incluye, mostraremos un error, si lo incluye pero el usuario no está vacío, mostramos un error pero si está vacío e incluye los parámetros solicitados, entonces lo creamos y le respondemos al cliente con la información.

Caso para cuando el usuario ya se creó
Caso para cuando el POST no está correcto
Caso para cuando se crea el usuario

Siguiente en PUT vamos a recibir la solicitud, seguimos la misma lógica que en POST pero, si en este caso, está vacío el nombre o el apellido significa que no ha sido creado el usuario, por lo tanto no hay nada que actualizar, sin embargo, si no está vacío y recibimos ambos parámetros, entonces, actualizamos al usuario.

Caso cuando el usuario no ha sido creado
Caso cuando algún campo no está incluido
Caso del usuario actualizado

Por último tenemos el DELETE que eliminará al usuario.

Caso para cuando el usuario no ha sido creado
Caso para cuando el usuario se elimina

Hasta aquí creo que ya se entendió la base de qué es cada uno, dejaré un ejemplo de ARC (es una extensión/aplicación de Chrome que yo utilizo pero pueden usar otras como POSTMAN o cualquier otro que les sirva para probar):

Esta es la vista del body
Y esta es la vista de los Headers

Ahora, vamos a modificar y simplificar un poco nuestro código usando app.route() de express, que nos permite agrupar la misma URL que responderá a distintos métodos ya que los vimos por separado y dejaremos este tutorial hasta aquí.

const express = require("express");
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
let usuario = {
 nombre:'',
 apellido: ''
};
let respuesta = {
 error: false,
 codigo: 200,
 mensaje: ''
};
app.get('/', function(req, res) {
 respuesta = {
  error: true,
  codigo: 200,
  mensaje: 'Punto de inicio'
 };
 res.send(respuesta);
});
app.route('/usuario')
 .get(function (req, res) {
  respuesta = {
   error: false,
   codigo: 200,
   mensaje: ''
  };
  if(usuario.nombre === '' || usuario.apellido === '') {
   respuesta = {
    error: true,
    codigo: 501,
    mensaje: 'El usuario no ha sido creado'
   };
  } else {
   respuesta = {
    error: false,
    codigo: 200,
    mensaje: 'respuesta del usuario',
    respuesta: usuario
   };
  }
  res.send(respuesta);
 })
 .post(function (req, res) {
  if(!req.body.nombre || !req.body.apellido) {
   respuesta = {
    error: true,
    codigo: 502,
    mensaje: 'El campo nombre y apellido son requeridos'
   };
  } else {
   if(usuario.nombre !== '' || usuario.apellido !== '') {
    respuesta = {
     error: true,
     codigo: 503,
     mensaje: 'El usuario ya fue creado previamente'
    };
   } else {
    usuario = {
     nombre: req.body.nombre,
     apellido: req.body.apellido
    };
    respuesta = {
     error: false,
     codigo: 200,
     mensaje: 'Usuario creado',
     respuesta: usuario
    };
   }
  }
  
  res.send(respuesta);
 })
 .put(function (req, res) {
  if(!req.body.nombre || !req.body.apellido) {
   respuesta = {
    error: true,
    codigo: 502,
    mensaje: 'El campo nombre y apellido son requeridos'
   };
  } else {
   if(usuario.nombre === '' || usuario.apellido === '') {
    respuesta = {
     error: true,
     codigo: 501,
     mensaje: 'El usuario no ha sido creado'
    };
   } else {
    usuario = {
     nombre: req.body.nombre,
     apellido: req.body.apellido
    };
    respuesta = {
     error: false,
     codigo: 200,
     mensaje: 'Usuario actualizado',
     respuesta: usuario
    };
   }
  }
  
  res.send(respuesta);
 })
 .delete(function (req, res) {
  if(usuario.nombre === '' || usuario.apellido === '') {
   respuesta = {
    error: true,
    codigo: 501,
    mensaje: 'El usuario no ha sido creado'
   };
  } else {
   respuesta = {
    error: false,
    codigo: 200,
    mensaje: 'Usuario eliminado'
   };
   usuario = { 
    nombre: '', 
    apellido: '' 
   };
  }
  res.send(respuesta);
 });
app.use(function(req, res, next) {
 respuesta = {
  error: true, 
  codigo: 404, 
  mensaje: 'URL no encontrada'
 };
 res.status(404).send(respuesta);
});
app.listen(3000, () => {
 console.log("El servidor está inicializado en el puerto 3000");
});

De esta forma agrupamos la URL /usuario y ya podemos utilizar sus métodos ya que por ejemplo, pensemos que la URL /usuario no debería poder eliminar usuarios, borramos el .delete() de estos y su contenido y listo, el DELETE queda desactivado de esta URL y así, si hacemos un módulo por ejemplo, podemos exportar estas rutas y hacer nuestro código más sencillo.

¿Qué se puede mejorar en este código?, que detecte si el tipo de contenido no sea application/json nos rechace, una forma más sencilla de setear la respuesta con una función, meter un servicio de autorización, entre muchas, muchas, muchas otras.

Conclusión

En conclusión un API REST es una interfaz donde podemos conectar nuestras aplicaciones para obtener, crear, actualizar o eliminar datos de forma sencilla, ya sea nosotros como clientes, o como proveedores del servicio permitir a otros realizar estas acciones gracias a los distintos métodos básicos (o principales) de HTTP (POST, GET, PUT, DELETE).

Se puede reutilizar la misma dirección para hacer distintas acciones.

GET sirve para obtener, POST para crear, PUT para actualizar y DELETE para eliminar.

Aprendimos a inicializar una app en NodeJS y a crear un servidor en Express y usarlo en su forma básica.

Y antes de irnos, te dejo una tarea, investiga qué es un servidor HTTP (para saber más de Express), investiga acerca de NPM para saber acerca de las dependencias, investiga más acerca de la autenticación en APIs REST y sigue adelante con qué son las APIs REST investigando más y más.

Por el momento espero te haya gustado este tutorial, recuerda dejar tu Clap, comentar si tienes dudas (o me equivoqué en alguna parte) y seguir adelante desarrollando tu propia API REST para tu servicio :).

¿Cuál es tu reacción?
+1
0
+1
0
+1
0
+1
1
+1
3
Total
0
Shares
Publicación anterior

Subiendo miles y miles de archivos a GitHub fácilmente con Git+Bash

Siguiente Publicación

Creando un componente de breadcrumbs (migas de pan) en Angular

Publicaciones Relacionadas