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 :
- Un proxy — un minuscule endpoint serveur qui détient votre clé et met les réponses en cache.
- 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 contiennentteam_nameettotal_points. - Constructeurs.
/standings/seasons/{year}/manufacturersrenvoie des lignesmanufacturer_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.