Tecnología

Cómo está construido este sitio, qué almacena y qué números son medidos en lugar de calculados aquí.

Renderizado

Next.js 15 con el App Router, React 19 y TypeScript en modo estricto. El estilo es Tailwind v4 con la paleta declarada como tokens de tema en una única hoja de estilos; no hay biblioteca de componentes ni de gráficos. Los cinco tipos de gráficos son SVG escrito a mano, así que se renderizan en el servidor y heredan las variables de tema, que es también la razón de que el modo claro no cueste nada en tiempo de ejecución.

Cada página bajo el segmento de idioma se renderiza por petición y no estáticamente. Es una decisión deliberada, no un descuido: la cabecera muestra estado por sesión (quién ha iniciado sesión, sus marcadores), así que el layout de idioma establece dynamic = "force-dynamic" y todo el subárbol renuncia a la generación estática. El idioma vive en la URL; el middleware lo negocia desde una cookie y luego desde Accept-Language, y copia el idioma elegido a una cabecera de la petición para que el layout raíz pueda establecer <html lang>.

Las páginas nunca llaman directamente a la API oficial. Llaman a un único módulo de servicio que decide por petición si servir la API en vivo, la caché local o, cuando no hay token configurado, datos de demostración deterministas — y devuelve esa decisión junto con los datos para que la interfaz pueda etiquetarla.

El índice local

La persistencia es node:sqlite, el binding de SQLite integrado en Node 22 y posteriores. No hay módulo nativo que compilar, ni servidor de base de datos que operar, ni ORM: toda la capa de datos es un archivo de SQL más un puñado de pequeños helpers tipados. El archivo vive donde apunte DATABASE_PATH, por defecto ./data/brawlinsights.db, y se abre con journaling WAL, claves foráneas activadas y un busy timeout de cinco segundos.

El esquema se crea con CREATE TABLE IF NOT EXISTS en cada arranque. Como eso nunca añade una columna a una tabla existente, los campos nuevos se aplican en un paso aparte que inspecciona cada tabla y emite los ALTER TABLE que falten, de modo que una instalación existente incorpora las columnas nuevas en vez de tener que desecharse.

Guarda la parte que la API oficial no nos da:

  • el directorio de jugadores y clubes que hace posible la búsqueda por nombre;
  • el historial de nombres y de clubes, reconstruido detectando cambios entre sondeos;
  • instantáneas de progresión por hora y por jugador, de donde salen los gráficos de tendencia;
  • los registros de batalla archivados y los agregados diarios acumulados a partir de ellos;
  • los aspectos equipados y la tabla de nombres de rango de Competitivo aprendida de los perfiles observados;
  • y todo aquello de lo que se encarga el propio sitio: las cuentas y las sesiones, marcadores, historial de visitas, imágenes subidas, chat y mensajes directos, publicaciones de los tablones, informes de errores y registros de moderación asociados a ellas. La sección 2 de la política de privacidad genera esa lista a partir del esquema en lugar de repetirla aquí.

Hablar con la API oficial

Un módulo envuelve api.brawlstars.com/v1. Cada respuesta se guarda también en la caché de datos de Next con una ventana de revalidación por endpoint y una etiqueta de caché, y los perfiles de jugador y club se reutilizan además desde la copia de SQLite durante un TTL corto antes de hacer siquiera una petición nueva. Cuando una petición falla por cualquier motivo distinto de 404, se sirve la última copia guardada en vez de romper la página; un 404 significa que la cuenta ya no existe, así que la página informa de que no se encontró y la cola de actualización retira esa etiqueta del índice.

PeticiónRevalidar tras
Perfil de jugador120 s
Registro de batallas60 s
Club, miembros del club300 s
Clasificaciones900 s
Rotación de eventos600 s
Plantel de brawlers86400 s

Los tokens de la API están ligados a la IP pública que hace las peticiones, así que un 403 accessDenied.invalidIp es con diferencia el fallo más común en producción. Se detecta específicamente y se muestra con una pista en lenguaje llano en el endpoint de salud en vez de registrarse como un error genérico.

La cola de actualización

La API no tiene canal de push ni filtro de «cambiado desde», así que la única forma de estar al día es sondear — y sondear todo por igual sería lento y derrochador. En su lugar, cada jugador indexado cae en una banda de prioridad, y un registro solo es candidato a actualización cuando es más viejo de lo que su banda permite. Los candidatos se procesan por banda más alta primero y registro más antiguo primero.

hotmarcado por cualquier cuenta, o visto en la última hora2 min
warmvisto en el último día15 min
coldtodo lo demás que alguna vez se haya indexado6 h

En esa pasada de perfiles, el registro de batallas se descarga junto con el perfil solo en las bandas hot y warm: es poco probable que una cuenta fría haya jugado desde la última vez que miramos, y cada registro es una segunda petición. Los clubes tienen su propio barrido con el umbral warm y los clubes marcados primero, y actualizar a un jugador actualiza también su club, lo que mantiene al día las páginas de club sin una tercera cola.

Los registros de batalla, sin embargo, no están confinados a esas bandas. Una pasada de cosecha aparte — normalmente la mayor parte de cada ciclo — recorre todo el índice según cuánto hace que se recogió el registro de cada cuenta, cuentas frías incluidas, descargando el registro de cualquiera que no se haya cosechado dentro de HARVEST_INTERVAL (30 min). Una cuenta que nadie mira sigue jugando partidas, y esas partidas son lo que hace crecer la muestra de las tier lists: esta pasada, y no la cola de perfiles, es la que construye el corpus.

Cada petición saliente toma primero un token de un cubo que se rellena a BS_API_RPS tokens por segundo (por defecto 8) hasta una capacidad de BS_API_BURST (por defecto 16). Como el cubo es un único objeto compartido, el ritmo se mantiene por muchos ciclos o actualizaciones manuales que se solapen; quien encuentra el cubo vacío espera exactamente el tiempo que necesita en vez de dar vueltas.

Un ciclo se ejecuta o bien dentro del proceso — un intervalo cada SYNC_INTERVAL_MS (por defecto 30 s) gastando como mucho SYNC_BUDGET peticiones (por defecto 100), repartidas normalmente en más o menos la mitad para la cosecha de registros de batalla, cerca de un tercio para perfiles de jugador y el resto para clubes, invirtiéndose a favor de los perfiles mientras más de mil cuentas recién descubiertas esperen su primera captura de perfil — o bien, donde la plataforma no puede sostener un intervalo en segundo plano, mediante un planificador externo que llama a /api/cron. Los ciclos solapados regresan de inmediato en vez de duplicar el ritmo de peticiones, la primera ejecución tras el arranque se escalona unos segundos al azar para que una tormenta de reinicios no golpee la API a la vez, y un 429 o un 403 de IP inválida corta el ciclo antes de tiempo en vez de aporrear una puerta cerrada. Una pasada de limpieza cada seis horas elimina las batallas archivadas de más de 90 días, los agregados de más de 120 días y las instantáneas de más de un año.

El botón de «actualizar ahora» de una página de jugador o club salta todos los TTL a través de un endpoint aparte con un enfriamiento de 20 segundos por etiqueta, así que mantener pulsado el botón no puede convertirse en un amplificador. /api/health informa del modo en vivo o demo, los tamaños del índice, el tamaño del archivo, la profundidad de la cola y el último error.

El archivo de batallas

La API solo devuelve las últimas 25 batallas de un jugador y no hay endpoint de historial. Todo lo que en este sitio mira más atrás de 25 partidas existe porque esas ventanas se archivan: cada visita a un perfil y cada actualización de la cola guarda lo que sea nuevo, con claves que hacen que ver la misma batalla dos veces sea una operación nula.

Al archivarse, cada batalla se acumula también en una tabla de agregados diarios con clave de día, modo, mapa, tramo y brawler, contando elecciones, victorias, empates y premios de jugador estelar. Se actualizan cuatro filas por batalla — el mapa real y un mapa sintético all, cada uno escrito dos veces: una bajo el tramo grueso (ranked o trophies) y otra bajo la banda exacta (ranked:masters, trophies:500-750). Las filas del mapa all son lo que convierte una tier list de todo un modo en una única lectura indexada en vez de un escaneo, y las filas de banda son lo que hace que los filtros por rango y por trofeos de las tier lists sean reales y no decorativos. Se omiten las batallas amistosas y aquellas en las que no puede identificarse el brawler del jugador.

Supervivencia no tiene campo de victoria o derrota, solo una posición, así que terminar en la mitad superior cuenta como victoria: top 5 de 10 en solitario, top 3 de 5 en dúo, top 2 de 3 en el resto. Esa regla se aplica de forma consistente en tasas de victoria, tier lists y desgloses por brawler.

Como el archivo se construye con las cuentas que esta instancia ha visto, es una muestra, no la población completa de partidas — y una muestra sesgada hacia las cuentas que la gente consulta. Eso afecta mucho más a los recuentos absolutos que a la forma relativa.

Cómo se puntúan las tier lists

Ordenar por tasa de victorias bruta pone arriba al brawler minoritario que tuvo una buena semana con 40 partidas. Ordenar por tasa de elección solo re-etiqueta la popularidad. La puntuación mezcla ambas, con la tasa de victorias descontada según cuánto nos la creemos realmente.

rows          = { b : picks(b) ≥ 200 }
globalWinRate = Σ wins / Σ picks

shrunk        = (wins + globalWinRate * 1500) / (picks + 1500)
pickRate      = picks / total picks
popularity    = log10(1 + pickRate * rows * 3) / log10(4)

score         = (shrunk - globalWinRate) * 100 + popularity * 1.8

El 1500 es un prior expresado en pseudobatallas: un brawler con 200 elecciones es arrastrado casi del todo hacia la media global, mientras que uno con 30.000 conserva esencialmente su propia tasa de victorias. Es la idea de la contracción bayesiana empírica, aplicada de forma tosca pero transparente. El término de popularidad es logarítmico, de modo que un brawler elegido diez veces más a menudo vale una bonificación modesta y no diez veces la puntuación.

Los niveles se cortan después a partir de la distribución de puntuaciones y no de umbrales fijos: se calculan la media y la desviación típica sobre los brawlers que califican, y cada brawler se coloca según cuántas desviaciones típicas queda por encima o por debajo de la media — S en +1,25, A en +0,5, B en −0,25, C en −1 y D por debajo. Una consecuencia que conviene saber: los niveles son relativos al meta actual, así que una lista siempre tiene nivel S aunque el meta esté plano.

Una tier list solo se construye con datos reales cuando la ventana seleccionada acumula al menos 5.000 elecciones (la ventana por defecto es de siete días). Por debajo, la página recurre al generador determinista y se marca a sí misma como datos de demostración en vez de mostrar una clasificación segura derivada de unos cientos de partidas.

Directo de la API

Esto se lee de la respuesta oficial y se muestra tal cual. La API publica más de lo que modelan la mayoría de los wrappers comunitarios, así que varios números que otros sitios estiman aquí son medidos.

  • Trofeos, máximo de trofeos, nivel de experiencia, victorias en 3c3 / solitario / dúo
  • Índice de rango de Competitivo y su nombre visible, elo de Competitivo, id de temporada
  • Máximos de rango y elo de Competitivo, de la temporada e históricos
  • Nivel total de prestigio, fama y rango de fama
  • Por brawler: poder, rango, trofeos, prestigio, racha de victorias actual y máxima
  • Por brawler: gadgets, habilidades estelares, equipamientos, hipercargas, aspecto equipado
  • Pertenencia a club, plantilla del club y roles de los miembros, trofeos requeridos
  • Clasificaciones por país y la rotación de eventos en vivo
  • Las últimas 25 batallas por jugador, con modo, mapa, tipo, resultado y cambio de trofeos
  • El propio plantel de brawlers, descargado por un script de sincronización a un archivo generado — sincronizado por última vez el 15/09/2026

Derivado aquí

Esto no existe en la API de ninguna forma. Se calcula a partir de lo que esta instancia ha observado, y cada elemento está etiquetado allí donde aparece.

  • Historial de nombres y de clubes — construido comparando sondeos sucesivos
  • Tendencias de trofeos, elo, prestigio y fama — de la tabla de instantáneas horarias
  • Tier lists, tasas de victoria, tasas de elección y tasas de jugador estelar — del archivo de batallas
  • Ranking de uso de aspectos — contado sobre el campo de aspecto equipado de los perfiles indexados
  • La tabla de nombres de rango de Competitivo — aprendida de perfiles observados, no codificada
  • Tiempo de juego estimado — a partir de victorias y nivel de experiencia, con un margen aproximado de ±15%, ya que la API no expone ningún reloj
  • La línea temporal de la puntuación de Competitivo — las batallas de Competitivo no llevan delta de puntuación, así que victorias y derrotas se modelan a unos ±30 para reconstruir la forma de una temporada
  • Rareza y clase por brawler — los únicos campos del plantel que la API omite, buscados por nombre en una tabla incluida y dejados como unknown en vez de adivinarse
  • Las tablas de las calculadoras (costes de mejora, cambio de trofeos, probabilidades de Obsequios Estelares, Camino de Trofeos) — transcritas del juego, instantánea 2026-09-16

Lo que sigue siendo modelado

Algunas páginas aún no están respaldadas por datos reales, y lo dicen en la propia página en vez de presentar una suposición en silencio:

  • Distribución de Competitivo. La página está conectada a la consulta real sobre el rango de Competitivo registrado en cada perfil totalmente indexado, y recurre a una escalera modelada solo mientras menos de 200 perfiles indexados tengan rango: por debajo de eso la población es tan pequeña y sesgada que la forma engañaría. Cuando recurre al modelo lo dice, indicando el recuento que tiene.
  • Tier lists con selección escasa. Un modo, mapa o tramo con menos de 5.000 elecciones archivadas recurre a la distribución modelada en vez de clasificar un puñado de partidas. La página indica el recuento que tiene y el que necesita.
  • Pins, tarjetas de batalla y títulos de perfil. No están en la API de ninguna forma, así que sus páginas de ranking son demostraciones del diseño tras un aviso explícito.
  • Modo demo. Sin token de API configurado, todo el sitio funciona con un generador determinista sembrado con la etiqueta que buscaste, así que la misma etiqueta produce siempre los mismos números. Cada página afectada lleva un banner de aviso.

Especificación de búsqueda explica qué significa el índice para los resultados de búsqueda.

Tecnología · BrawlPeek