Captura y depura los datos de las peticiones

Captura los datos de las peticiones para entender el tipo de errores que estás cometiendo en el front-end

Captura y depura los datos de las peticiones

A veces los errores en tu código de cliente no son tan evidentes como cuando obtienes una pantalla en blanco porque nada de tu código JavaScript funciona. A veces el problema con tu aplicación es que, ante ciertas acciones que tu usuario puede realizar, estás formando incorrectamente una petición al servidor. El código del cliente funciona, no obtienes ningún error de JS en la consola, pero a tu back-end no le gusta realmente lo que le envías.

Con OpenReplay, puedes capturar la comunicación cliente-servidor como parte de tu repetición de sesión estándar y revisarla más tarde. Así que veamos cómo podemos hacer eso y qué tipo de beneficio podemos obtener de ello.

Para los propósitos de este tutorial, he creado una sencilla aplicación de React que hace uso de la Bored API. Esta es una API muy sencilla que devuelve una sugerencia de actividad aleatoria basada en algunos parámetros. Así que creé la “I’m bored App”, que se ve así:

Y puedes encontrarla en vivo en Netlify por aquí, o si quieres echar un vistazo al código para verlo en detalle, está totalmente disponible en GitHub.

Esta aplicación está formada por 2 componentes, el componente SearchForm, que se encarga de renderizar esos 2 campos y el botón, además de enviar la petición real a la API. Y el componente Suggestion simplemente renderiza la sugerencia dentro de una caja de aspecto agradable.

Me voy a centrar en el primero, ya que es el único que envía peticiones usando la función fetch.

Ten en cuenta que esta técnica también funciona para cualquier petición realizada con Axios.

Echemos un vistazo rápido al componente para entender qué está haciendo.

El código del componente SearchForm

Section titled El código del componente SearchForm

Este no es un componente complejo, pero hay una sección que es especialmente relevante para este caso de uso en particular, así que echémosle un vistazo rápido.

import { Container, Col, Form, Row, Button } from 'react-bootstrap';

const SearchForm = ({setResult, fetcher}) => {
const getSomething = async (evt) => {
    evt.preventDefault()
    let form = evt.target

    const API_URL = "/api/activity?"
    let getParams = {}
    if(form.participants.value !== '') {
      getParams.participants = form.participants.value
    }

    if(form.priceRange.value !== '') {
      let prices = form.priceRange.value.split("_")
      getParams.minprice = prices[0]
      getParams.maxprice = prices[1]
    }

    let results = await fetcher(API_URL + new URLSearchParams(getParams), {
        mode: 'no-cors'
    })
    setResult(await results.json())

    return false
  }

    return (
        <Container>
        <Form onSubmit={getSomething}>
          <Row>
            <Col>
          <Form.Group controlId='participants' >
            <Form.Label>Participants</Form.Label>
            <Form.Control type='text' name="totalParticipants" placeholder='Leave empty if you dont care...'></Form.Control>
          </Form.Group>
          </Col>
            <Col>
          <Form.Group controlId='priceRangeId'>
            <Form.Label>Price range</Form.Label>
            <Form.Select name="priceRange" >
              <option value="" >Select one or leave empty if you dont care</option>
              <option value="0.0">Free</option>
              <option value="0.1_0.5">Cheap</option>
              <option value="0.6_1.0">Expensive</option>
            </Form.Select>
          </Form.Group>
          </Col>
          </Row>
          <Row className='m-3'>
            <Col>
            <Form.Group>
              <Button variant="primary" type="submit">Get me something!</Button>
            </Form.Group>
            </Col>
          </Row>
        </Form>
      </Container>
    )
}

export default SearchForm

Fíjate en la función getSomething, ahí es donde ocurre la mayor parte de la magia. La función se llama cuando se dispara el evento submit del formulario. Cuando eso ocurre, la función obtiene el evento sintético con el formulario vinculado dentro de la propiedad target. Simplemente capturamos los valores de cada uno de los filtros (el campo de entrada y el desplegable) y luego ejecutamos la petición con la función fetch. Fíjate en que la URL no apunta directamente al endpoint de la BoredAPI. Eso es porque, para que la petición funcione y no quede bloqueada debido a las restricciones de CORS, he configurado un proxy en el backend para redirigir todas las peticiones de /api a la API real.

Ahora que has visto el código, echemos un vistazo a lo que obtendrías si instalaras el tracker de OpenReplay sin el plugin de fetch.

Captura de datos habitual con OpenReplay

Section titled Captura de datos habitual con OpenReplay

Para este ejemplo, voy a usar la versión NPM del paquete; si no sabes cómo hacer eso, consulta la documentación y luego vuelve aquí.

Esta es la interfaz de la repetición de sesión por defecto. Fíjate en cómo, en la mitad inferior, ya he seleccionado la pestaña “Network”, pero aunque muestra las peticiones que se están realizando, no hay detalles sobre ellas. Incluso si haces clic en una de ellas, obtendrás los detalles mínimos disponibles:

Entonces, ¿qué podemos hacer? Puedes habilitar la captura de la información de las peticiones con el objeto de opciones de red. Veamos eso.

Capturando los datos de las peticiones en tus repeticiones de sesión

Section titled Capturando los datos de las peticiones en tus repeticiones de sesión

Para esto, todo lo que tenemos que hacer es añadir una opción de configuración cuando instancias el tracker. Así que ahora, cuando escribas la línea new tracker(...), añadirás una nueva propiedad:

import Tracker from '@openreplay/tracker';

const tracker = new Tracker({
  projectKey: "<your project key>",
  network: {
    capturePayload: true //start capturing the payload of every request
  }
});

Eso es todo lo que necesitamos hacer; a partir de ahora, cada vez que realices una petición, los datos serán registrados por el tracker. Ahora despliega el cambio, prueba la aplicación, cierra la pestaña y espera un par de minutos. La sesión debería aparecer pronto y puedes pulsar el botón “play”.

Inspeccionando la comunicación cliente-servidor

Section titled Inspeccionando la comunicación cliente-servidor

Para los fines del ejemplo, veamos también un problema que empecé a observar después de publicar la aplicación.

Fíjate en el cuadro de advertencia que obtengo en este caso:

Como el desarrollador que programó esto, sé qué hacer para probarlo y entender dónde está el bug. Sin embargo, como usuario, el error no me dice realmente mucho, y puede que no sea capaz de comunicarlo de una forma que el equipo de desarrollo pueda entender. Así que, en cambio, como usuario, simplemente puedo quejarme a la empresa de que su aplicación no funciona, y tú, como el desarrollador responsable de la aplicación, puedes echar un vistazo a mi sesión e inspeccionar la petición que envió el cliente y la respuesta del servidor.

Mira ahora la interfaz de la repetición de sesión. Dentro de la pestaña Network puedes ver las peticiones que hemos estado haciendo a la API externa.

Todo lo que tenemos que hacer ahora es encontrar el momento en el que obtenemos la respuesta de error y mirar las peticiones que se están realizando. Lo más probable es que veas el problema dentro de los detalles de la petición. En nuestro caso, el error dice “Failed to query due to error in arguments”, lo que significa que cuando seleccionamos la opción “Free” en el desplegable, no estamos enviando una petición válida. Así que echemos un vistazo a sus detalles.

¿Puedes ver el problema? Déjame ayudarte:

Sí, estoy enviando un undefined como valor del atributo maxprice. Pasé eso por alto por completo en mi lógica, y lo detecté mientras inspeccionaba la petición. De acuerdo, es una corrección fácil ahora que sé dónde está el problema, pero gracias a este proceso habría podido o bien generar un informe de error muy detallado, o bien ayudar directamente al desarrollador a identificar y resolver el problema sin tener que probarlo yo mismo y reproducir el incidente.

Poniendo a prueba la privacidad

Section titled Poniendo a prueba la privacidad

Muy bien, llevemos este ejemplo un poco más lejos; supongamos que también necesito el número de teléfono de mi usuario para esta petición. Claramente no lo necesito, pero sígueme la corriente un minuto.

Añadiré el campo al formulario y actualizaré el código para capturar ese valor y enviarlo como parte de la petición.

El HTML del formulario solo consiste en añadir un nuevo elemento Col así:

<!-- previous code -->
<Col>
    <Form.Group controlId='phoneNumber'>
        <Form.Label>Phone Number</Form.Label>
        <Form.Control type='number' name="phoneNumber" placeholder='Enter your phone number here please'></Form.Control>
    </Form.Group>
</Col>
<!-- rest of the code -->

Y añadir el contenido de este campo a la petición real solo necesita una única línea de código:

getParams.phonenumber = form.phoneNumber.value

Ahora, ¿qué ocurre si usamos este nuevo código y capturamos una sesión con OpenReplay? Pues bien, dos cosas:

  1. La repetición que realmente ves depurará automáticamente el contenido del campo del número de teléfono y no se mostrará a nadie que la esté viendo.
  2. La información de la petición capturada por el plugin, sin embargo, sí mostrará el valor.

La siguiente captura de pantalla muestra lo que acabo de describir:

En la sección derecha de la pantalla puedes ver el número de teléfono completo. Esto ocurre porque, mientras que el tracker normal puede entender que el campo del número de teléfono es un campo numérico, no capturará su entrada por si acaso el número representa información personal. Pero en el lado de la petición, no podemos realmente hacer esa suposición, ya que el desarrollador podría haber hecho cualquier cosa con los datos, o incluso con el nombre del parámetro. Así que la pregunta entonces es: ¿podemos proteger la privacidad de nuestro usuario con este plugin?

Y la respuesta, me complace informar, es: SÍ podemos.

Depurando los datos de la petición

Section titled Depurando los datos de la petición

Si vuelves al inicio de este tutorial, cuando configuré las opciones de red, verás que no dije nada sobre la depuración. Sin embargo, como parte de esas opciones puedes especificar un callback destinado a depurar los datos. Este callback recibe un único atributo con ambos, el objeto de la petición y el de la respuesta. Luego puedes elegir editarlos como quieras; no afectarán a la petición real, pero cambiarán la forma en que se muestran los datos en la interfaz de OpenReplay.

Por ejemplo, digamos que quiero cambiar el atributo “phonenumber” y eliminar los números para evitar filtrar esa información. Esto se puede hacer así:

const tracker = new Tracker({
  projectKey: "<your project id>",
  network: {
    capturePayload: true,
    sanitizer: (data) => { //we change the content of the "phonenumber" parameter from the url
      data.url = data.url.replace(/phonenumber=([0-9]+)/, "phonenumber=XXXXXX")
      return data
    }
    }
});

Como puedes ver, el cambio es sencillo, reemplazamos solo los números de este atributo, así que ahora la petición se ve así en nuestra interfaz:

Ahora los datos de tu usuario están seguros una vez más.

Si quieres echar un vistazo al código para ver este ejemplo en detalle, puedes encontrarlo aquí en GitHub. Si tienes algún problema configurando el plugin de Fetch o el propio Tracker, ponte en contacto con nosotros en nuestra comunidad de Slack y pregunta directamente a nuestros desarrolladores.