¡Qué onda, desarrolladores! Bienvenidos a otro tutorial en 8devmx. Cuando vamos empezando en el mundo del desarrollo web, es muy común pensar que para crear una aplicación funcional necesitamos levantar un servidor complejo, configurar bases de datos gigantescas y gestionar usuarios desde cero. Sin embargo, el ecosistema web moderno nos ofrece una alternativa padrísima: consumir servicios que ya existen mediante APIs desde el navegador.

Imagina que quieres crear un tablero de ajedrez, un monitor de clima o un buscador de películas. En lugar de crear un servidor completo que almacene las partidas o el clima, puedes construir un cliente web ligero usando únicamente HTML, CSS y JavaScript para consultar la información directamente de plataformas existentes (como Lichess, OpenWeather o TMDB). En este artículo aprenderás a conectar tu sitio web con un servicio externo mediante la Fetch API y Async/Await de JavaScript.


¿Qué es un cliente web y por qué construir uno?

Un cliente web es la interfaz gráfica con la que interactúa el usuario final en el navegador. Se encarga de procesar las acciones de la persona (dar clic en un botón, escribir texto) y enviar o solicitar datos a un servidor remoto a través de una API REST.

Las ventajas de trabajar con este enfoque al iniciar son gigantescas:

  1. Ahorro de recursos: No necesitas gastar en hosting costoso ni mantener bases de datos complejas.
  2. Enfoque en el Frontend: Te permite perfeccionar tu lógica en JavaScript, manipulación del DOM y diseño sin preocuparte por el backend.
  3. Aprovechamiento de ecosistemas: Puedes consumir datos en tiempo real de plataformas consolidadas.

Paso a paso: Creando un cliente en JavaScript

Vamos a construir una aplicación cliente sencilla que consulta el perfil y las estadísticas públicas de cualquier usuario en la plataforma de ajedrez Lichess a través de su API pública.

Paso 1: La estructura HTML

Primero, necesitamos una interfaz básica con un campo de texto para escribir el usuario y un contenedor donde renderizaremos la información.

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Mi Cliente de Ajedrez - 8devmx</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div class="app-container">
    <h1>Buscador de Perfiles Lichess</h1>
    <div class="search-box">
      <input type="text" id="usernameInput" placeholder="Escribe un usuario (ej. lichess)">
      <button id="searchBtn">Buscar</button>
    </div>
    <div id="statusMessage"></div>
    <div id="profileResult" class="card hidden">
      <h2 id="profileTitle"></h2>
      <p id="profileBio"></p>
      <ul id="statsList"></ul>
    </div>
  </div>
  <script src="app.js"></script>
</body>
</html>

Paso 2: Un toque de estilo con CSS

Agregamos unos estilos limpios para dar un aspecto profesional a nuestra aplicación.

body {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  background-color: #121212;
  color: #e0e0e0;
  display: flex;
  justify-content: center;
  padding-top: 50px;
}

.app-container {
  background-color: #1e1e1e;
  padding: 30px;
  border-radius: 8px;
  width: 100%;
  max-width: 450px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.5);
}

.search-box {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

input {
  flex: 1;
  padding: 10px;
  border-radius: 4px;
  border: 1px solid #333;
  background-color: #2a2a2a;
  color: #fff;
}

button {
  padding: 10px 20px;
  border: none;
  background-color: #007acc;
  color: white;
  border-radius: 4px;
  cursor: pointer;
}

button:hover {
  background-color: #005999;
}

.card {
  background-color: #252526;
  padding: 15px;
  border-radius: 6px;
  border-left: 4px solid #007acc;
}

.hidden {
  display: none;
}

Paso 3: Consumiendo la API con JavaScript

Ahora viene lo chido. Crearemos la lógica en JavaScript para consultar los datos usando fetch() y manejar la asincronía mediante async/await.

// app.js
const searchBtn = document.getElementById('searchBtn');
const usernameInput = document.getElementById('usernameInput');
const statusMessage = document.getElementById('statusMessage');
const profileResult = document.getElementById('profileResult');
const profileTitle = document.getElementById('profileTitle');
const profileBio = document.getElementById('profileBio');
const statsList = document.getElementById('statsList');

searchBtn.addEventListener('click', () => {
  const username = usernameInput.value.trim();
  if (username) {
    obtenerPerfilLichess(username);
  } else {
    statusMessage.textContent = 'Por favor escribe un nombre de usuario validote.';
  }
});

async function obtenerPerfilLichess(usuario) {
  // Limpiamos mensajes previos y ocultamos resultados
  statusMessage.textContent = 'Cargando datos...';
  profileResult.classList.add('hidden');
  statsList.innerHTML = '';

  try {
    // Hacemos la petición a la API pública de Lichess
    const respuesta = await fetch(`https://lichess.org/api/user/${usuario}`);

    if (!respuesta.ok) {
      if (respuesta.status === 404) {
        throw new Error('El usuario no existe en Lichess.');
      }
      throw new Error('Ocurrió un error al consultar la API.');
    }

    const datos = await respuesta.json();
    mostrarPerfil(datos);
    statusMessage.textContent = '';
  } catch (error) {
    statusMessage.textContent = `Error: ${error.message}`;
  }
}

function mostrarPerfil(datos) {
  profileTitle.textContent = datos.username;
  profileBio.textContent = datos.bio || 'Este usuario no tiene biografía.';

  // Mostramos algunas puntuaciones si están disponibles
  if (datos.perfs) {
    if (datos.perfs.blitz) {
      const li = document.createElement('li');
      li.textContent = `Blitz: ${datos.perfs.blitz.rating} ELO`;
      statsList.appendChild(li);
    }
    if (datos.perfs.rapid) {
      const li = document.createElement('li');
      li.textContent = `Rápida: ${datos.perfs.rapid.rating} ELO`;
      statsList.appendChild(li);
    }
  }

  profileResult.classList.remove('hidden');
}

Errores comunes al consumir APIs

  1. No verificar respuesta.ok: fetch() no lanza una excepción por fallos HTTP como un error 404 o 500. Es necesario comprobar respuesta.ok manualmente antes de procesar el JSON.
  2. Problemas con CORS (Cross-Origin Resource Sharing): No todas las APIs permiten ser consultadas directamente desde un navegador web. Asegúrate de usar APIs públicas que tengan las cabeceras CORS habilitadas.
  3. Olvidar el bloque try/catch: Si el usuario no tiene conexión a internet o la API cae, tu script fallará feo si no envuelves las llamadas asíncronas en un bloque de control de errores.

Buenas prácticas de desarrollo

  • Mostrar estados de carga: Avisa siempre a la persona usuaria que la petición está en proceso. Nadie quiere dar clic y ver una pantalla congelada.
  • Separar responsabilidades: Fíjate cómo dividimos la lógica de petición (obtenerPerfilLichess) de la lógica de presentación (mostrarPerfil). Esto hace que tu código sea legible y fácil de mantener.
  • Respeta los límites de peticiones (Rate Limits): No hagas llamadas consecutivas innecesarias para evitar que la API bloquee la dirección IP del usuario.

¡Ponte a prueba! Ejercicio práctico

Modifica el código fuente que acabamos de escribir para agregar una nueva funcionalidad:

  1. Agrega un elemento <p> en el HTML para mostrar si el usuario está actualmente en línea (datos.online).
  2. Muestra el número total de partidas jugadas utilizando la propiedad datos.count.all.

¡Inténtalo en tu editor local y verás lo rápido que puedes construir herramientas útiles con pura lógica cliente!


Fuente original e inspiración

Este artículo fue inspirado por la experiencia de desarrollo compartida por Ivan Kulkin en su publicación “I built a browser chess client that can play Lichess games without becoming another chess server”, donde demuestra el potencial de crear clientes web autónomos que interactúan directamente con plataformas existentes.