← Tous les guides

Créer un tableau de classement du championnat en direct

Affichez le championnat pilotes MotoGP, Moto2 ou Moto3 sur votre site via un petit proxy avec cache qui garde votre clé RapidAPI hors du navigateur.

10 min de lecture

Un tableau de classement est le widget MotoGP le plus courant sur les sites d’actualité, blogs de fans et pages de clubs. Ce guide en construit un en deux parties :

  1. Un proxy — un minuscule endpoint serveur qui détient votre clé et met les réponses en cache.
  2. Un widget — du HTML et du JavaScript simples qui affichent le tableau depuis le proxy.

1. Le proxy

Le navigateur ne doit jamais voir votre clé RapidAPI : la page parle donc à votre propre endpoint. Cet exemple utilise Node.js 18+ et Express ; les mêmes 30 lignes fonctionnent comme fonction Netlify, Vercel ou Cloudflare.

// server.js — npm install express
import express from "express";

const HOST = "motogp-api-championship-data-statistics.p.rapidapi.com";
const KEY = process.env.RAPIDAPI_KEY;
if (!KEY) throw new Error("RAPIDAPI_KEY is not set");

const CATEGORIES = new Set(["MOTOGP", "MOTO2", "MOTO3"]);
const TTL_MS = 60 * 60 * 1000; // 1 hour
const cache = new Map();

async function cached(path) {
  const hit = cache.get(path);
  if (hit && Date.now() - hit.at < TTL_MS) return hit.body;
  const res = await fetch(`https://${HOST}${path}`, {
    headers: { "X-RapidAPI-Key": KEY, "X-RapidAPI-Host": HOST },
  });
  if (!res.ok) throw new Error(`Upstream ${res.status}`);
  const body = await res.json();
  cache.set(path, { at: Date.now(), body });
  return body;
}

const app = express();

app.get("/api/standings", async (req, res) => {
  const category = String(req.query.category ?? "MOTOGP").toUpperCase();
  if (!CATEGORIES.has(category)) return res.status(400).json({ error: "Unknown category" });
  try {
    const { data: season } = await cached("/seasons/latest");
    const { data } = await cached(`/standings/seasons/${season.year}/riders?category=${category}`);
    res.set("Cache-Control", "public, max-age=300").json({ season: season.year, category, rows: data });
  } catch (err) {
    res.status(502).json({ error: "Standings are unavailable right now" });
  }
});

app.use(express.static("public"));
app.listen(3000, () => console.log("http://localhost:3000"));

Deux détails comptent :

  • Validez les entrées. Seules les trois catégories connues sont transmises, pour que personne ne puisse se servir de votre proxy pour dépenser votre quota sur des endpoints arbitraires.
  • Mettez en cache. Un appel amont par heure et par catégorie sert tous les visiteurs.

2. Le widget

<!-- public/index.html -->
<div class="standings">
  <div role="tablist">
    <button data-category="MOTOGP" aria-selected="true">MotoGP</button>
    <button data-category="MOTO2">Moto2</button>
    <button data-category="MOTO3">Moto3</button>
  </div>
  <table>
    <caption></caption>
    <thead>
      <tr><th>Pos</th><th>Rider</th><th>Team</th><th>Wins</th><th>Pts</th></tr>
    </thead>
    <tbody></tbody>
  </table>
</div>

<script type="module">
  const tbody = document.querySelector(".standings tbody");
  const caption = document.querySelector(".standings caption");

  function cell(text) {
    const td = document.createElement("td");
    td.textContent = text; // textContent, never innerHTML, for data from an API
    return td;
  }

  async function load(category) {
    caption.textContent = "Loading…";
    const res = await fetch(`/api/standings?category=${category}`);
    if (!res.ok) {
      caption.textContent = "Standings are unavailable right now.";
      tbody.replaceChildren();
      return;
    }
    const { season, rows } = await res.json();
    caption.textContent = `${season} ${category} riders' championship`;
    tbody.replaceChildren(
      ...rows.map((r) => {
        const tr = document.createElement("tr");
        tr.append(
          cell(r.position),
          cell(`#${r.rider_number} ${r.rider_name}`),
          cell(r.team_name),
          cell(r.wins),
          cell(r.total_points),
        );
        return tr;
      }),
    );
  }

  document.querySelectorAll("[data-category]").forEach((button) => {
    button.addEventListener("click", () => {
      document.querySelectorAll("[data-category]").forEach((b) => b.setAttribute("aria-selected", b === button));
      load(button.dataset.category);
    });
  });

  load("MOTOGP");
</script>

Lancez RAPIDAPI_KEY=... node server.js et ouvrez http://localhost:3000.

Variantes

  • Écart avec le leader. Ajoutez une colonne avec rows[0].total_points - r.total_points.
  • Championnat par équipes. Pointez une seconde route vers /standings/seasons/{year}/teams — les lignes contiennent team_name et total_points.
  • Constructeurs. /standings/seasons/{year}/manufacturers renvoie des lignes manufacturer_name.
  • Sites statiques. Récupérez le classement pendant votre étape de build (Astro, Next.js, Hugo) et reconstruisez après chaque week-end de course — aucun proxy nécessaire.
Guide suivant → Pages de profil de pilote avec statistiques de carrière