¡No esperes! Burlarse de la API

Índice
  1. Mirage: el marco burlón
  2. Configuración de espejismo
  3. Cómo funciona
  4. ¡Hagamos peticiones!
  5. Solicitudes burlonas
  6. Trabajar con datos dinámicos
  7. ¿Rutas dinámicas? ¡Seguro!
  8. Bien, integremos esto en una aplicación existente.
  9. Terminando

Hoy en día tenemos un acoplamiento flojo entre el front-end y el back-end de las aplicaciones web. Por lo general, son desarrollados por equipos separados y mantener sincronizados esos equipos y la tecnología no es fácil. Para resolver parte de este problema, podemos "falsificar" el servidor API que la tecnología de back-end normalmente crearía y desarrollaría como si la API o los puntos finales ya existieran.

El término más común utilizado para crear un componente simulado o "falsificado" es burla. La burla le permite simular la API sin (idealmente) cambiar la interfaz. Hay muchas maneras de lograr burlarse, y esto es lo que hace que resulte tan aterrador para la mayoría de las personas, al menos en mi opinión.

Veamos cómo debería ser una buena simulación de API y cómo implementar una API simulada en una aplicación nueva o existente.

Tenga en cuenta que la implementación que estoy a punto de mostrar es independiente del marco, por lo que se puede utilizar con cualquier marco o aplicación básica de JavaScript.

Mirage: el marco burlón

El enfoque burlón que vamos a utilizar se llama Mirage, que es algo nuevo. He probado muchos marcos simulados y recientemente descubrí este, y ha cambiado las reglas del juego para mí.

Mirage se comercializa como un marco fácil de usar que viene con una interfaz moderna. Funciona en su navegador, del lado del cliente, interceptando XMLHttpRequesty recuperando solicitudes.

Pasaremos a crear una aplicación sencilla con una API simulada y cubriremos algunos problemas comunes a lo largo del camino.

Configuración de espejismo

Creemos una de esas aplicaciones estándar de tareas pendientes para demostrar la burla. Usaré Vue como mi marco preferido pero, por supuesto, puedes usar otra cosa ya que estamos trabajando con un enfoque independiente del marco.

Entonces, continúa e instala Mirage en tu proyecto:

# Using npmnpm i miragejs -D
# Using Yarnyarn add miragejs -D

Para empezar a utilizar Mirage, necesitamos configurar un “servidor” (entre comillas, porque es un servidor falso). Antes de pasar a la configuración, cubriré la estructura de carpetas que encontré que funciona mejor.

/├── public├── src│   ├── api│   │   └── mock│   │       ├── fixtures│   │       │   └── get-tasks.js│   │       └── index.js│   └── main.js├── package.json└── package-lock.json

En un mockdirectorio, abra un nuevo index.jsarchivo y defina su servidor simulado:

// api/mock/index.jsimport { Server } from 'miragejs';
export default function ({ environment = 'development' } = {}) {  return new Server({    environment,
    routes() {      // We will add our routes here    },  });}

El argumento de entorno que estamos agregando a la firma de la función es solo una convención. Podemos pasar en un entorno diferente según sea necesario.

Ahora, abra el archivo de arranque de su aplicación. En nuestro caso, este es el src/main.jsarchivo ya que estamos trabajando con Vue. Importe su createServerfunción y llámela en el entorno de desarrollo.

// main.jsimport createServer from './mock'
if (process.env.NODE_ENV === 'development') {    createServer();}

Estamos usando la process.env.NODE_ENVvariable de entorno aquí, que es una variable global común. El condicional permite que Mirage sea sacudido en producción, por lo tanto, no afectará su paquete de producción.

¡Eso es todo lo que necesitamos para configurar Mirage! Es este tipo de facilidad lo que hace que el DX de Mirage sea tan agradable.

Nuestra createServerfunción es establecer el developmententorno de forma predeterminada para simplificar este artículo. En la mayoría de los casos, esto será predeterminado testya que, en la mayoría de las aplicaciones, llamará createServeruna vez en el modo de desarrollo pero muchas veces en los archivos de prueba.

Cómo funciona

Antes de hacer nuestra primera solicitud, cubramos rápidamente cómo funciona Mirage.

Mirage es un marco de burla del lado del cliente , lo que significa que todas las burlas ocurrirán en el navegador, lo que Mirage hace usando la biblioteca Pretender. Pretender reemplazará temporalmente XMLHttpRequestlas configuraciones nativas y de recuperación, interceptará todas las solicitudes y las dirigirá a un pequeño servicio simulado al que se conecta Mirage.

Si abre DevTools y accede a la pestaña Red, no verá ninguna solicitud de Mirage. Esto se debe a que Mirage intercepta y maneja la solicitud (a través de Pretender en el back-end). Mirage registra todas las solicitudes, algo a lo que llegaremos en un momento.

¡Hagamos peticiones!

Creemos una solicitud a un /api/taskspunto final que devolverá una lista de tareas que mostraremos en nuestra aplicación de tareas pendientes. Tenga en cuenta que estoy usando axios para recuperar los datos. Esa es solo mi preferencia personal. Nuevamente, Mirage funciona con bibliotecas nativas XMLHttpRequest, Fetch y cualquier otra biblioteca.

// components/tasks.vueexport default {  async created() {    try {      const { data } = await axios.get('/api/tasks'); // Fetch the data      this.tasks = data.tasks;    } catch(e) {      console.error(e);    }  }};

Al abrir su consola de JavaScript, debería haber un error de Mirage allí:

Mirage: Your app tried to GET '/api/tasks', but there was no route defined to handle this request.

Esto significa que Mirage se está ejecutando, pero aún no se ha simulado el enrutador. Resolvamos esto agregando esa ruta.

Solicitudes burlonas

Dentro de nuestro mock/index.jsarchivo hay un routes()gancho. Los controladores de ruta nos permiten definir qué URL debe manejar el servidor Mirage.

Para definir un controlador de enrutador, debemos agregarlo dentro de la routes()función.

// mock/index.jsexport default function ({ environment = 'development' } = {}) {    // ...    routes() {      this.get('/api/tasks', () = ({        tasks: [          { id: 1, text: "Feed the cat" },          { id: 2, text: "Wash the dishes" },          //...        ],      }))    },  });}

El routes()gancho es la forma en que definimos nuestros controladores de ruta. El uso de un this.get()método nos permite simular GETsolicitudes. El primer argumento de todas las funciones de solicitud es la URL que estamos manejando y el segundo argumento es una función que responde con algunos datos.

Como nota, Mirage acepta cualquier tipo de solicitud HTTP y cada tipo tiene la misma firma:

this.get('/tasks', (schema, request) = { ... });this.post('/tasks', (schema, request) = { ... });this.patch('/tasks/:id', (schema, request) = { ... });this.put('/tasks/:id', (schema, request) = { ... });this.del('/tasks/:id', (schema, request) = { ... });this.options('/tasks', (schema, request) = { ... });

Discutiremos los parámetros schemay requestde la función de devolución de llamada en un momento.

Con esto, hemos burlado exitosamente nuestra ruta y deberíamos ver dentro de nuestra consola una respuesta exitosa por parte de Mirage.

Trabajar con datos dinámicos

No será posible intentar agregar una nueva tarea pendiente en nuestra aplicación porque nuestros datos en la GETrespuesta tienen valores codificados. La solución de Mirage a esto es que proporcionan una capa de datos liviana que actúa como una base de datos. Arreglemos lo que tenemos hasta ahora.

Al igual que el routes()anzuelo, Mirage define un seeds()anzuelo. Nos permite crear datos iniciales para el servidor. Voy a mover los GETdatos al seeds()gancho donde los enviaré a la base de datos de Mirage.

seeds(server) {  server.db.loadData({    tasks: [      { id: 1, text: "Feed the cat" },      { id: 2, text: "Wash the dishes" },    ],  })},

Moví nuestros datos estáticos del GETmétodo al seeds()enlace, donde esos datos se cargan en una base de datos falsa. Ahora, necesitamos refactorizar nuestro GETmétodo para devolver datos de esa base de datos. En realidad, esto es bastante sencillo: el primer argumento de la función de devolución de llamada de cualquier route()método es el esquema.

this.get('/api/tasks', (schema) = {  return schema.db.tasks;})

Ahora podemos agregar nuevas tareas pendientes a nuestra aplicación realizando una POSTsolicitud:

async addTask() {  const { data } = await axios.post('/api/tasks', { data: this.newTask });  this.tasks.push(data);  this.newTask = {};},

Nos burlamos de esta ruta en Mirage creando un POST /api/taskscontrolador de ruta:

this.post('/tasks', (schema, request) = {})

Usando el segundo parámetro de la función de devolución de llamada, podemos ver la solicitud enviada.

Dentro de la requestBodypropiedad están los datos que enviamos. Eso significa que ahora está disponible para que creemos una nueva tarea.

this.post('/api/tasks', (schema, request) = {  // Take the send data from axios.  const task = JSON.parse(request.requestBody).data
  return schema.db.tasks.insert(task)})

El tamaño idde la tarea lo establecerá la base de datos de Mirage de forma predeterminada. Por lo tanto, no es necesario realizar un seguimiento de los identificadores y enviarlos con su solicitud, como en un servidor real.

¿Rutas dinámicas? ¡Seguro!

Lo último que hay que cubrir son las rutas dinámicas. Nos permiten usar un segmento dinámico en nuestra URL, lo cual es útil para eliminar o actualizar una sola tarea pendiente en nuestra aplicación.

Nuestra solicitud de eliminación debe ir a /api/tasks/1, /api/tasks/2y así sucesivamente. Mirage nos proporciona una manera de definir un segmento dinámico en la URL, como este:

this.delete('/api/tasks/:id', (schema, request) = {  // Return the ID from URL.  const id = request.params.id;
  return schema.db.tasks.remove(id);})

Usando dos puntos ( :) en la URL es como definimos un segmento dinámico en nuestra URL. Después de los dos puntos, especificamos el nombre del segmento que, en nuestro caso, se llama idy se asigna al ID de una tarea pendiente específica. Podemos acceder al valor del segmento a través del request.paramsobjeto, donde el nombre de la propiedad corresponde al nombre del segmento: request.params.id. Luego usamos el esquema para eliminar un elemento con ese mismo ID de la base de datos de Mirage.

Si te has dado cuenta, todas mis rutas hasta ahora tienen el prefijo api/. Escribir esto una y otra vez puede resultar engorroso y es posible que desee hacerlo más fácil. Mirage ofrece la namespacepropiedad que puede ayudar. Dentro del gancho de rutas, podemos definir la namespacepropiedad para no tener que escribirla cada vez.

routes() { // Prefix for all routes. this.namespace = '/api';
 this.get('/tasks', () = { ... }) this.delete('/tasks/:id', () = { ... }) this.post('/tasks', () = { ... })}

Bien, integremos esto en una aplicación existente.

Hasta ahora, todo lo que hemos visto integra Mirage en una nueva aplicación. Pero ¿qué tal si agregamos Mirage a una aplicación existente? Mirage lo tiene cubierto para que no tenga que burlarse de toda su API.

Lo primero que hay que tener en cuenta es que agregar Mirage a una aplicación existente generará un error si el sitio realiza una solicitud que no es manejada por Mirage. Para evitar esto, podemos decirle a Mirage que pase por todas las solicitudes no controladas.

routes() {  this.get('/tasks', () = { ... })    // Pass through all unhandled requests.  this.passthrough()}

Ahora podemos desarrollar sobre una API existente y Mirage maneja solo las partes faltantes de nuestra API.

Mirage puede incluso cambiar la URL base de la que captura las solicitudes. Esto es útil porque, normalmente, un servidor no reside en localhost:3000un dominio personalizado, sino en un dominio personalizado.

routes() { // Set the base route. this.urlPrefix = 'https://devenv.ourapp.example';
 this.get('/tasks', () = { ... })}

Ahora, todas nuestras solicitudes apuntarán al servidor API real, pero Mirage las interceptará como lo hizo cuando lo configuramos con una nueva aplicación. Esto significa que la transición de Mirage a la API real es bastante fluida: elimine la ruta del servidor simulado y todo estará listo.

Terminando

En el transcurso de cinco años, he usado muchos marcos burlones, pero nunca me gustó ninguna de las soluciones que existen. Eso fue hasta hace poco, cuando mi equipo se enfrentó a la necesidad de una solución burlona y descubrí Mirage.

Otras soluciones, como el servidor JSON de uso común, son procesos externos que deben ejecutarse junto con el front-end. Además, a menudo no son más que un servidor Express con funciones de utilidad además. El resultado es que los desarrolladores front-end como nosotros necesitamos saber sobre middleware, NodeJS y cómo funcionan los servidores... cosas que muchos de nosotros probablemente no queremos manejar. Otros intentos, como Mockoon, tienen una interfaz compleja y carecen de funciones muy necesarias. Hay otro grupo de frameworks que sólo se utilizan para pruebas, como el popular SinonJS. Desafortunadamente, estos marcos no se pueden utilizar para burlarse del comportamiento habitual.

Mi equipo logró crear un servidor funcional que nos permite escribir código de front-end como si estuviéramos trabajando con un back-end real. Lo hicimos escribiendo el código base de front-end sin ningún proceso o servidor externo necesario para ejecutarse. Por eso amo Mirage. Es realmente sencillo de configurar, pero lo suficientemente potente como para manejar cualquier cosa que se le presente. Puede usarlo para aplicaciones básicas que devuelven una matriz estática a aplicaciones back-end completas por igual, independientemente de si se trata de una aplicación nueva o existente.

Hay mucho más en Mirage más allá de las implementaciones que cubrimos aquí. Puede encontrar un ejemplo práctico de lo que cubrimos en GitHub. (Dato curioso: ¡Mirage también funciona con GraphQL!) Mirage tiene documentación bien escrita que incluye un montón de tutoriales paso a paso, así que asegúrese de consultarla.

SUSCRÍBETE A NUESTRO BOLETÍN 
No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

Deja un comentario

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

Subir

Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion