Volver a la lista

Buscador de información genética: creación de una barra de búsqueda mediante la combinación de una API y JSON.

Aprenda paso a paso cómo llamar a una API de base de datos de genes pública con `fetch` y cómo analizar la respuesta JSON para crear un widget de búsqueda.

Intermedio
|
90min
|
Verificado (2026-07)
Búsqueda de genesREST APIJSONfetchasíncronoAnotación de genes.
Progreso0/19 (0%)

Buscador de información genética: creación de un cuadro de búsqueda mediante API y JSON

Al finalizar este tema

Podrás crear un cuadro de búsqueda que, al ingresar el nombre de un gen, obtenga información sobre su función, ubicación y alias de una base de datos pública y la muestre en forma de tarjeta, combinando las API y el JSON que aprendiste en clase. Así, podrás familiarizarte con el código que permite "utilizar una base de datos masiva creada por otros en tu propia aplicación".

Este artículo es un ejemplo didáctico. Utiliza una API de anotación de genes gratuita y pública como material de referencia para aprender el patrón universal de obtención de datos externos desde la web.


"¿Qué gen era este?" — El problema de tener que buscar en varios sitios cada vez

Cuando se realizan experimentos, a menudo se conoce el nombre de un gen, pero se olvidan los detalles. ¿En qué cromosoma se encuentra TP53? ¿Cuál es el alias oficial de BRCA1?

En esos casos, abrimos el navegador, vamos al sitio web de la base de datos pública, escribimos en el cuadro de búsqueda y revisamos la página de resultados. No es un problema si se hace una o dos veces, pero ¿qué pasa si hay que verificar 50 genes? Se abre y cierra 50 pestañas, lo cual es un infierno.

En ese momento, surge el pensamiento del desarrollador: "Ese sitio web también muestra datos de una base de datos. ¿No podría consultar directamente esa base de datos?" La respuesta es sí. Esa es la API.


Primero, veamos el producto final (haciendo funcionar la caja negra)

El cuadro de búsqueda que vamos a crear funcionará de la siguiente manera: al ingresar el nombre del gen en el cuadro de búsqueda...

text
┌──────────────────────────────────────┐
│  🔍  [ TP53            ]  [Buscar]      │
├──────────────────────────────────────┤
│  TP53   (tumor protein p53)            │
│  ─────────────────────────────         │
│  📍 Ubicación: 17p13.1                  │
│  🏷️ Alias    : p53, LFS1, BCC7          │
│  🧬 Resumen  : ciclo celular, supresión │
└──────────────────────────────────────┘

No es necesario abrir un sitio web; la información aparece directamente en la barra de búsqueda. Para lograr esto, solo se necesitan dos conceptos: dónde buscar la información (API) y cómo leer la respuesta (JSON).


¿De qué componentes está compuesto este programa? (Desglose de componentes)

text
Widget de búsqueda de genes
   ┌────────────────────────────────────────┐
   │ [Consulta] Construir URL ── pieza: API     │ ← La construyes tú ★
   │              │                           │
   │              ▼                           │
   │ [Envío] Llamada con fetch ─ pieza: fetch  │ ← Se proporciona completa
   │              │            + asincronía    │
   │              ▼                           │
   │ [Análisis] Parsear respuesta ─ pieza: JSON│ ← La construyes tú ★
   │              │                           │
   │              ▼                           │
   │ [Salida] Renderizar tarjeta                │
   └────────────────────────────────────────┘
Componente¿Dónde se aprendió?¿Qué hace en esta herramienta?
APIapi-basicsCrear la URL para indicar "qué y dónde preguntar"
fetch/asíncronoajax-fetch, sync-vs-asyncRealizar la solicitud y esperar
JSONjson-data-formatExtraer los valores necesarios de la respuesta recibida

📌 Si estos conceptos son nuevos para ti (enlace en la parte superior)

Los dos conceptos nuevos que construiremos son API y JSON. fetch, que realiza la comunicación real, se proporciona como un producto terminado porque es una herramienta.

🔎 API en una frase (cajón — api-basics) Una API es un menú de restaurante. No puedes entrar directamente en la cocina (la base de datos), pero si haces un pedido en el menú (la API) de acuerdo con las reglas, obtendrás un plato (datos). Aprenderemos a hacer un pedido de "dame esta información genética" de acuerdo con las reglas del menú.


Paso 1: Construir la URL para indicar qué preguntar ★ (API)

✍️ Sección para completar. Componente = API. Objetivo: convertir "dame esta información genética" en una URL que la API pueda entender.

La forma de hacer un pedido a la API suele ser a través de una URL. Se puede construir "dirección + lo que se desea" siguiendo las reglas. Por ejemplo, supongamos que una API de genes pública tiene estas reglas.

text
https://api-ejemplo.org/query?q={nombre_gen}&fields={campos_deseados}
  • Después de q=, el término de búsqueda (nombre del gen).
  • Después de fields=, los elementos que desea recibir (separados por comas).

Si se crea manualmente, puede haber errores tipográficos y de codificación. Por lo tanto, se crea una función para ensamblar la URL de forma segura. Lo importante aquí es encodeURIComponent: envuelve el término de búsqueda para que la URL no se dañe, incluso si el término de búsqueda contiene espacios o caracteres especiales.

javascript
const API_BASE = "https://example-gene-api.org/query";

function buildQueryUrl(geneName, fields = ["symbol", "name", "genomic_pos", "alias", "summary"]) {
  const q = encodeURIComponent(geneName.trim());
  const f = encodeURIComponent(fields.join(","));
  return `${API_BASE}?q=${q}&fields=${f}`;
}

// Verificación
console.assert(
  buildQueryUrl("TP53") === "https://example-gene-api.org/query?q=TP53&fields=symbol%2Cname%2Cgenomic_pos%2Calias%2Csummary",
  "Falló la construcción de la URL básica"
);
// También debe codificar de forma segura consultas con espacios
console.assert(
  buildQueryUrl("  TP 53  ").includes("q=TP%2053"),
  "Falló la codificación de espacios"
);

El segundo assert es clave. Aunque el usuario introduzca un espacio, como en TP 53, este se codificará de forma segura como TP%2053. Si no se utiliza encodeURIComponent, este espacio dividirá la URL y la solicitud fallará. La mitad de los errores en las llamadas a la API se deben a que se omite esta codificación.

🤔 Pregunta para la reflexión ¿Por qué se incluyó geneName.trim()? Piense en cómo la API interpretaría esto si un usuario introduce "TP53 " (con un espacio al final) en la barra de búsqueda y no se utiliza la función trim. (Es la misma idea que la normalización de secuencias en el primer módulo: asegúrese de que todo tenga el formato correcto antes de enviarlo).


Paso 2: realizar la solicitud (fetch, código completo proporcionado)

La parte de la realización de la solicitud y la espera de la respuesta se proporciona como código completo, ya que es una herramienta. Es igual que lo que aprendimos en ajax-fetch y sync-vs-async: fetch + async/await.

javascript
async function callGeneApi(geneName) {
  const url = buildQueryUrl(geneName);
  const response = await fetch(url);        // ← Viaje de red; await espera la respuesta
  if (!response.ok) {
    throw new Error(`Error de API: ${response.status}`);
  }
  return await response.json();             // ← Convierte el cuerpo en un objeto JSON
}

🔎 ¿Por qué usar async/await? (Diagrama — sincrónico vs. asíncrono) Las solicitudes de red tardan tiempo (ida y vuelta al servidor). Durante este tiempo, el navegador no debería bloquearse. await permite "esperar a que esto termine, pero sin bloquear la interfaz". Es como pedir comida en un restaurante y recibir un dispositivo de vibración para sentarse a esperar: no tienes que quedarte de pie hasta que llegue la comida.

La última línea de esta función, response.json(), es el puente hacia el siguiente paso. Convierte el bloque de texto enviado por el servidor en un objeto JavaScript que podemos manejar. El procesamiento de ese objeto es el paso 3.


Paso 3: extraer solo lo necesario de la respuesta ★ (JSON)

✍️ Sección para completar manualmente. Componente = JSON. Objetivo: extraer de forma segura los valores que necesitamos de una respuesta compleja.

El JSON que devuelve una API suele ser mucho más grande y complejo de lo que necesitamos. Supongamos que la respuesta real tiene este aspecto.

json
{
  "hits": [
    {
      "symbol": "TP53",
      "name": "tumor protein p53",
      "genomic_pos": { "chr": "17", "start": 7668402, "end": 7687550 },
      "alias": ["p53", "LFS1", "BCC7"],
      "summary": "Gen implicado en la regulación del ciclo celular y la supresión tumoral."
    }
  ]
}

Aquí, usaremos el nombre, la ubicación, el alias y el resumen en la tarjeta. Creamos una función que busca en el JSON, extrae solo los valores necesarios y los organiza en un objeto limpio. En este caso, es importante tener en cuenta — manejar de forma segura los valores que podrían no estar presentes. Algunos genes no tienen alias y algunas respuestas no tienen resultados.

javascript
function parseGeneResponse(data) {
  const hit = data.hits && data.hits[0];
  if (!hit) {
    return null;                          // Sin resultados; lo gestiona el llamador
  }
  const pos = hit.genomic_pos || {};
  return {
    symbol: hit.symbol || "?",
    name: hit.name || "",
    location: pos.chr ? `chr${pos.chr}:${pos.start}-${pos.end}` : "Sin información",
    aliases: Array.isArray(hit.alias) ? hit.alias : [],   // Array vacío si no hay alias
    summary: hit.summary || "No hay información de resumen.",
  };
}

Ahora, lo validaremos con una respuesta simulada. De esta manera, podemos verificar si la lógica de análisis es correcta sin necesidad de una red real. Este es un buen diseño: separar el procesamiento de datos (análisis) puro de la red facilita las pruebas.

javascript
const mockResponse = {
  hits: [{
    symbol: "TP53",
    name: "tumor protein p53",
    genomic_pos: { chr: "17", start: 7668402, end: 7687550 },
    alias: ["p53", "LFS1", "BCC7"],
    summary: "Gen implicado en la regulación del ciclo celular y la supresión tumoral.",
  }],
};

const parsed = parseGeneResponse(mockResponse);
console.assert(parsed.symbol === "TP53", "Falló el análisis del símbolo");
console.assert(parsed.location === "chr17:7668402-7687550", "Falló la construcción de la ubicación");
console.assert(parsed.aliases.length === 3, "Falló el análisis de los alias");

// También debe gestionar de forma segura una respuesta sin resultados
console.assert(parseGeneResponse({ hits: [] }) === null, "Falló el manejo de resultados vacíos");
console.assert(parseGeneResponse({}) === null, "Falló el manejo de un objeto vacío");

// Un gen sin alias tampoco debe provocar un error
const noAlias = parseGeneResponse({ hits: [{ symbol: "X", genomic_pos: { chr: "1", start: 1, end: 2 } }] });
console.assert(Array.isArray(noAlias.aliases) && noAlias.aliases.length === 0, "Falló el manejo de un gen sin alias");

Los últimos tres assert separan a los principiantes de los expertos. Si solo se manejan los casos que funcionan, eres un principiante; si se manejan también los casos inexistentes, vacíos o incorrectos, eres un experto. Si hit.alias no está presente y se usa directamente, la aplicación fallará con undefined.length. Una sola línea de Array.isArray(...) ? ... : [] evita ese error.

🤔 Indicación de autoexplicación En data.hits && data.hits[0], ¿por qué se usó &&? Si se recibe una respuesta donde data.hits no está presente (undefined), explica qué error ocurriría si no se aplicara esta protección. (Este es un tema similar al de "valores que pueden no estar presentes" en el primer artículo).


Uniendo las piezas: un widget completo

Ahora, unimos la construcción de la URL (API) → la llamada (fetch) → el análisis (JSON) → y el renderizado.

javascript
function renderGeneCard(gene) {
  if (!gene) return "No hay resultados de búsqueda.";
  return [
    `${gene.symbol}  (${gene.name})`,
    `📍 Ubicación: ${gene.location}`,
    `🏷️ Alias: ${gene.aliases.join(", ") || "ninguno"}`,
    `🧬 Resumen: ${gene.summary}`,
  ].join("\n");
}

async function searchGene(geneName) {
  try {
    const raw = await callGeneApi(geneName);
    const gene = parseGeneResponse(raw);
    return renderGeneCard(gene);
  } catch (e) {
    return `Se produjo un error: ${e.message}`;
  }
}

// Comprueba también el renderizado con un mock, sin necesidad de red
const card = renderGeneCard(parseGeneResponse(mockResponse));
console.assert(card.includes("TP53"), "Falló el renderizado de la tarjeta");
console.assert(card.includes("chr17:7668402-7687550"), "Falló el renderizado de la ubicación");
console.assert(renderGeneCard(null) === "No hay resultados de búsqueda.", "Falló el renderizado de un resultado vacío");

searchGene Mira cómo try/catch lo envuelve todo. Las redes pueden fallar en cualquier momento (el servidor se cae, la conexión a Internet se interrumpe). En lugar de que la aplicación se bloquee por completo, mostrar "Se produjo un error" es la diferencia entre una herramienta de uso real y un juguete.


Otras opciones (reflexión multipaso)

  • Búsqueda simultánea de varios genes: Buscar 50 genes uno por uno con await es lento (secuencial). Lanzarlos simultáneamente con Promise.all([...]) es mucho más rápido. Compromiso: Puede sobrecargar el servidor, por lo que las API públicas tienen un límite de frecuencia de solicitudes por segundo.
  • Caché: Si se vuelve a buscar el mismo gen, en lugar de llamar a la API de nuevo, se utiliza el resultado almacenado. La misma idea que el diccionario (secuencia → valor) de la sección anterior, pero aquí con nombre del gen → resultado.
  • Proxy de backend: Si un servicio requiere una clave de API, exponer la clave en el navegador es un riesgo. En la práctica, se utiliza nuestro propio servidor como intermediario. → Continúa en la sección de aplicación Backend de búsqueda de genes.

Punto clave: Llamar directamente a una API externa desde el frontend es bueno para obtener resultados rápidos, pero hay mejores opciones en términos de velocidad (solicitudes simultáneas), reutilización (caché) y seguridad (clave).

Próximos pasos (enlaces de salida en la parte inferior)


Ponlo en práctica (problema independiente)

  1. Estado de carga: Muestra "Buscando..." mientras se realiza la búsqueda y, una vez finalizada, muestra los resultados. (Representa visualmente el "tiempo de espera" de la operación asíncrona).
  2. Añade caché: Guarda los genes que ya se han buscado en un objeto ({}) para reutilizarlos y evitar llamar a la API de nuevo. Utiliza console.assert para comprobar si la segunda llamada utiliza la caché.
  3. Varios genes: Recibe varios nombres de genes separados por comas y búscalos simultáneamente con Promise.all.
  4. Desafío: El formato de respuesta de la API puede cambiar, y genomic_pos puede devolver una matriz (múltiples posiciones). Modifica el código para que parseGeneResponse gestione correctamente tanto las matrices como los objetos.

Resumen

Hemos transformado la "molestia de buscar la información de los genes en el sitio web cada vez" en un widget de búsqueda que trae toda la información a mi aplicación con una sola API.

  • La API representa "qué y dónde preguntar" mediante una URL. (Consulta)
  • Fetch/asíncrono envía la solicitud y espera sin bloquear la pantalla. (Envío)
  • JSON extrae de forma segura solo los valores necesarios de una respuesta compleja, incluso si faltan algunos. (Interpretación)

La API y JSON pueden parecer abstractos cuando se aprenden por separado, pero al combinarlos, todos los conjuntos de datos públicos del mundo se convierten en los materiales de mi aplicación. Ya sea una base de datos de genes, una base de datos de artículos o una base de datos de estructuras de proteínas, el patrón siempre es el mismo: crear una URL de solicitud, enviarla y analizar la respuesta.

Este artículo es un ejemplo educativo general. En un servicio real, se agregarían autenticación, límites de solicitud, reintentos de errores y paginación. Puede agregar la versión detallada a esta estructura básica.

💬 Preguntas y comentarios

0 comentarios

Puedes publicar sin iniciar sesión. Los comentarios de invitados no pueden editarse ni eliminarse después.

0/2000

Cargando...