API Football avec Next JS PAge Router

Créer une application de classement des championnats de football

··19 min. read
Share on TwitterShare on Linkedin
Cover Image for API Football avec Next JS PAge Router

Dans le monde du football, qui évolue rapidement, il est essentiel pour les fans les plus assidus de se tenir au courant des derniers classements de la ligue. Construire une application de football debout avec Next.js et exploiter la puissance de l'API-Football peut fournir aux fans des informations en temps réel sur les classements des équipes, les points, les buts marqués, et bien plus encore. Dans cet article de blog, nous vous guiderons à travers le processus de création d'une application de classement de football dynamique et intuitive qui tiendra les fans informés et engagés tout au long de la saison.

Setup

Mise en place du projet

Installation de Next.js : Nous allons commencer par mettre en place un nouveau projet Next.js et explorer ses avantages pour la construction d'applications web interactives.

npx create-next-app@latest

Lors de l'installation, vous verrez les invites suivantes ; je n'utiliserai pas Typescript pour ce projet :

What is your project named? football-app
Would you like to use TypeScript? No
Would you like to use ESLint?  Yes
Would you like to use Tailwind CSS? Yes
Would you like to use `src/` directory? No
Would you like to use App Router? (recommended)  Yes
Would you like to customize the default import alias? No

Après avoir créé votre projet nommé "football-app" avec Next.js, configurons quelques paramètres supplémentaires :

ESLint :

ESLint est un outil puissant pour maintenir la qualité et la cohérence du code. L'activation d'ESLint vous aidera à détecter les erreurs potentielles et à appliquer les meilleures pratiques. C'est une excellente chose que vous ayez choisi d'utiliser ESLint dans votre projet.

Tailwind CSS :

Tailwind CSS est un framework CSS hautement personnalisable qui offre des classes utilitaires pour construire des interfaces utilisateur réactives et modernes. En choisissant d'utiliser Tailwind CSS, vous aurez accès à une large gamme de styles et de composants préconstruits pour améliorer l'interface utilisateur de votre application.

répertoire src/ :

Next.js vous permet d'organiser le code de votre application dans un répertoire src/ dédié. Cela peut être bénéfique pour les projets plus importants avec de nombreux fichiers et dossiers.

App Router :

L'App Router fourni par Next.js simplifie la navigation et le routage au sein de votre application, facilitant la gestion des différentes pages et vues.

Alias d'importation :

Par défaut, Next.js fournit un alias pour le répertoire src/, ce qui peut être pratique si vous décidez de l'utiliser dans le futur.

Next.js est maintenant livré avec un support intégré pour TypeScript, ESLint, et la configuration CSS Tailwind, ce qui facilite la mise en place de votre environnement de développement. Ces fonctionnalités vous aideront à écrire un code propre et de haute qualité et à créer des interfaces utilisateur visuellement attrayantes.

Préparez-vous à plonger dans le processus de développement et à libérer le potentiel de votre application de football avec Next.js !

Ouvrez le projet et lancez votre serveur de développement avec :

yarn dev
// or
npm run dev

Voici la page qui s'ouvrira, le modèle de base de Next JS

Construction

Ouvrez le fichier Page.js dans le répertoire de l'application et nettoyez-le comme suit :

import Image from "next/image"

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24"></main>
  )
}

Pour obtenir notre clé API, rendez-vous sur [RAPID API FOOTBALL] (https://rapidapi.com/api-sports/api/api-football/), inscrivez-vous gratuitement et obtenez vos clés API, c'est un freemium, 100 appels par jour, soyez prudents.

Tout d'abord, nous créons un composant appelé Headers, qui nous aidera à basculer entre les différentes ligues européennes de football. Nous créons un objet appelé leagues avec l'ID de la ligue, par exemple la Serie A est 135, nous passons cet ID à notre appel API et nous obtenons le classement de la Serie.

import React from "react"

function Header() {
  const leagues = {
    "Ligue 1 🇫🇷": 61,
    "Serie A 🇮🇹": 135,
    "Bundesliga 🇩🇪": 78,
    "Premier Leage 🇬🇧": 39,
    "Primera Division 🇪🇸": 140,
    "Primeira Liga 🇵🇹": 94,
    "Eredivise 🇳🇱": 88,
  }

  return (
    <div className="relative bg-white">
      <div className="mx-auto max-w-7xl px-4 sm:px-6">
        <div className="flex items-center justify-between border-b-2 border-gray-100 py-6 md:justify-start md:space-x-10">
          <div className="-my-2 -mr-2 md:hidden">
            <button
              type="button"
              className="inline-flex items-center justify-center rounded-md bg-white p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
              aria-expanded="false"
            >
              <span className="sr-only">Open menu</span>
            </button>
          </div>
          <nav className="flex space-x-12 overflow-auto">
            {Object.keys(leagues).map(function (key) {
              return (
                <div key={key} className="relative">
                  <button
                    value={leagues[key]}
                    type="button"
                    className="group inline-flex items-center rounded-md bg-white text-base font-medium text-gray-500 outline-none hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-green-300 focus:ring-offset-2"
                    aria-expanded="false"
                  >
                    <span>{key}</span>
                  </button>
                </div>
              )
            })}
          </nav>
        </div>
      </div>
    </div>
  )
}

export default Header

Ensuite, nous créerons une page dynamique dans laquelle nous passerons l'ID de la ligue en tant que paramètre et récupérerons les données de cette ligue.

Dans le dossier app, créez un dossier appelé [leagueID] et créez page.js à l'intérieur.


├── README.md
├── app.js
├── app
│   └── page.js
│   └── layout.js
│   └── global.css
│   └── favicon.ico
│   └── [leagueID]
│           └── page.js
└── public
    └── next.svg

Dans ce fichier, créez un composant de base pour l'instant

import React from "react"

function page() {
  return <div>PAGE</div>
}

export default page

Maintenant, pour accéder à cette page, nous devons mettre à jour notre fichier Header.js. Pour ce faire, nous ajoutons un lien pour accéder à la page /[leagueID]. Tout ce que nous avons à faire est d'ajouter un lien à la clé d'objet dans notre fichier Header.js.

import Link from "next/link"
import React from "react"

function Header() {
  const leagues = {
    "Ligue 1 🇫🇷": 61,
    "Serie A 🇮🇹": 135,
    "Bundesliga 🇩🇪": 78,
    "Premier Leage 🇬🇧": 39,
    "Primera Division 🇪🇸": 140,
    "Primeira Liga 🇵🇹": 94,
    "Eredivise 🇳🇱": 88,
  }

  return (
    <div className="relative bg-white">
      <div className="mx-auto max-w-7xl px-4 sm:px-6">
        <div className="flex items-center justify-between border-b-2 border-gray-100 py-6 md:justify-start md:space-x-10">
          <div className="-my-2 -mr-2 md:hidden">
            <button
              type="button"
              className="inline-flex items-center justify-center rounded-md bg-white p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
              aria-expanded="false"
            >
              <span className="sr-only">Open menu</span>
            </button>
          </div>
          <nav className="flex space-x-12 overflow-auto">
            {Object.keys(leagues).map(function (key) {
              return (
                <div key={key} className="relative">
                  <Link
                    href={`/${leagues[key]}`}
                    className="group inline-flex items-center rounded-md bg-white text-base font-medium text-gray-500 outline-none hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-green-300 focus:ring-offset-2"
                    aria-expanded="false"
                  >
                    <span>{key}</span>
                  </Link>
                </div>
              )
            })}
          </nav>
        </div>
      </div>
    </div>
  )
}

export default Header

Maintenant, si vous cliquez sur une ligue dans votre en-tête, vous devriez être dirigé vers la nouvelle page.

Concentrons-nous maintenant sur notre app/[leagueID]/page.js. Nous allons utiliser les paramètres de l'URL pour récupérer les données de la bonne ligue, Next JS nous permet d'obtenir les paramètres dans les props de la page, et grâce à cet identifiant nous pouvons faire un appel API aux différentes ligues.

Page URL params app/[leagueID]/page.js

URL /61

Params leagueID: '61'

import React from "react"

function Page({ params }) {
  const leagueID = params?.leagueID
  return (
    <div>
      <h1>League ID: {leagueID}</h1>
    </div>
  )
}

export default Page

Sur l'écran, nous affichons les paramètres leagueID 👇

Retournez dans notre éditeur et nous utiliserons les points de terminaison API-Football pour récupérer les derniers classements de la ligue, y compris les positions des équipes, les points, les victoires, les nuls et les défaites. Vous pouvez consulter la documentation RAPIDAPI pour connaître tous les points de terminaison existants.

Vous pouvez consulter [Next JS documentation] (https://nextjs.org/docs/app/building-your-application/data-fetching/fetching) pour savoir comment récupérer les données.

import React from "react"

async function getData(params) {
  const url = `https://api-football-v1.p.rapidapi.com/v3/standings?season=2022&league=${params}`
  const options = {
    method: "GET",
    headers: {
      "X-RapidAPI-Key": "YOUR-API-KEY",
      "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
    },
  }

  const res = await fetch(url, options)
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.

  // Recommendation: handle errors
  if (!res.ok) {
    // This will activate the closest `error.js` Error Boundary
    throw new Error("Failed to fetch data")
  }

  return res.json()
}

async function Page({ params }) {
  const data = await getData(params?.leagueID)

  const leagueInfos = data?.response[0]?.league

  console.log(leagueInfos)

  return (
    <div>
      <h1>League ID: </h1>
    </div>
  )
}

export default Page

Si nous cliquons sur Ligue 1 française, l'URL sera http://localhost:3000/61, l'ID de la ligue ici est 61, vous pouvez vérifier votre terminal, la console.log() est affichée ici parce que c'est une page serveur par défaut, Dans le terminal nous avons des données :

{
  "league": {
    "id": 61,
    "name": "Ligue 1",
    "country": "France",
    "logo": "https://media-3.api-sports.io/football/leagues/61.png",
    "flag": "https://media-2.api-sports.io/flags/fr.svg",
    "season": 2022,
    "standings": [
      [
        {
          "rank": 1,
          "team": {
            "id": 85,
            "name": "Paris Saint Germain",
            "logo": "https://media-3.api-sports.io/football/teams/85.png"
          },
          "points": 85,
          "goalsDiff": 49,
          "group": "Ligue 1",
          "form": "LDWWW",
          "status": "same",
          "description": "Promotion - Champions League (Group Stage: )",
          "all": {
            "played": 38,
            "win": 27,
            "draw": 4,
            "lose": 7,
            "goals": {
              "for": 89,
              "against": 40
            }
          },
          "home": {
            "played": 19,
            "win": 13,
            "draw": 2,
            "lose": 4,
            "goals": {
              "for": 45,
              "against": 25
            }
          },
          "away": {
            "played": 19,
            "win": 14,
            "draw": 2,
            "lose": 3,
            "goals": {
              "for": 44,
              "against": 15
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 2,
          "team": {
            "id": 116,
            "name": "Lens",
            "logo": "https://media-3.api-sports.io/football/teams/116.png"
          },
          "points": 84,
          "goalsDiff": 39,
          "group": "Ligue 1",
          "form": "WWWWW",
          "status": "same",
          "description": "Promotion - Champions League (Group Stage: )",
          "all": {
            "played": 38,
            "win": 25,
            "draw": 9,
            "lose": 4,
            "goals": {
              "for": 68,
              "against": 29
            }
          },
          "home": {
            "played": 19,
            "win": 17,
            "draw": 1,
            "lose": 1,
            "goals": {
              "for": 41,
              "against": 13
            }
          },
          "away": {
            "played": 19,
            "win": 8,
            "draw": 8,
            "lose": 3,
            "goals": {
              "for": 27,
              "against": 16
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 3,
          "team": {
            "id": 81,
            "name": "Marseille",
            "logo": "https://media-2.api-sports.io/football/teams/81.png"
          },
          "points": 73,
          "goalsDiff": 27,
          "group": "Ligue 1",
          "form": "LLLWL",
          "status": "same",
          "description": "Promotion - Champions League (Qualification: )",
          "all": {
            "played": 38,
            "win": 22,
            "draw": 7,
            "lose": 9,
            "goals": {
              "for": 67,
              "against": 40
            }
          },
          "home": {
            "played": 19,
            "win": 10,
            "draw": 4,
            "lose": 5,
            "goals": {
              "for": 35,
              "against": 24
            }
          },
          "away": {
            "played": 19,
            "win": 12,
            "draw": 3,
            "lose": 4,
            "goals": {
              "for": 32,
              "against": 16
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 4,
          "team": {
            "id": 94,
            "name": "Rennes",
            "logo": "https://media-2.api-sports.io/football/teams/94.png"
          },
          "points": 68,
          "goalsDiff": 30,
          "group": "Ligue 1",
          "form": "WWWWL",
          "status": "same",
          "description": "Promotion - Europa League (Group Stage: )",
          "all": {
            "played": 38,
            "win": 21,
            "draw": 5,
            "lose": 12,
            "goals": {
              "for": 69,
              "against": 39
            }
          },
          "home": {
            "played": 19,
            "win": 15,
            "draw": 0,
            "lose": 4,
            "goals": {
              "for": 43,
              "against": 14
            }
          },
          "away": {
            "played": 19,
            "win": 6,
            "draw": 5,
            "lose": 8,
            "goals": {
              "for": 26,
              "against": 25
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 5,
          "team": {
            "id": 79,
            "name": "Lille",
            "logo": "https://media-3.api-sports.io/football/teams/79.png"
          },
          "points": 67,
          "goalsDiff": 21,
          "group": "Ligue 1",
          "form": "DWWDL",
          "status": "same",
          "description": "Promotion - Europa Conference League (Qualification: )",
          "all": {
            "played": 38,
            "win": 19,
            "draw": 10,
            "lose": 9,
            "goals": {
              "for": 65,
              "against": 44
            }
          },
          "home": {
            "played": 19,
            "win": 13,
            "draw": 4,
            "lose": 2,
            "goals": {
              "for": 40,
              "against": 25
            }
          },
          "away": {
            "played": 19,
            "win": 6,
            "draw": 6,
            "lose": 7,
            "goals": {
              "for": 25,
              "against": 19
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 6,
          "team": {
            "id": 91,
            "name": "Monaco",
            "logo": "https://media-2.api-sports.io/football/teams/91.png"
          },
          "points": 65,
          "goalsDiff": 12,
          "group": "Ligue 1",
          "form": "LLLDW",
          "status": "same",
          "description": null,
          "all": {
            "played": 38,
            "win": 19,
            "draw": 8,
            "lose": 11,
            "goals": {
              "for": 70,
              "against": 58
            }
          },
          "home": {
            "played": 19,
            "win": 9,
            "draw": 3,
            "lose": 7,
            "goals": {
              "for": 37,
              "against": 33
            }
          },
          "away": {
            "played": 19,
            "win": 10,
            "draw": 5,
            "lose": 4,
            "goals": {
              "for": 33,
              "against": 25
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 7,
          "team": {
            "id": 80,
            "name": "Lyon",
            "logo": "https://media-2.api-sports.io/football/teams/80.png"
          },
          "points": 62,
          "goalsDiff": 18,
          "group": "Ligue 1",
          "form": "LWWLW",
          "status": "same",
          "description": null,
          "all": {
            "played": 38,
            "win": 18,
            "draw": 8,
            "lose": 12,
            "goals": {
              "for": 65,
              "against": 47
            }
          },
          "home": {
            "played": 19,
            "win": 10,
            "draw": 5,
            "lose": 4,
            "goals": {
              "for": 35,
              "against": 19
            }
          },
          "away": {
            "played": 19,
            "win": 8,
            "draw": 3,
            "lose": 8,
            "goals": {
              "for": 30,
              "against": 28
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 8,
          "team": {
            "id": 99,
            "name": "Clermont Foot",
            "logo": "https://media-3.api-sports.io/football/teams/99.png"
          },
          "points": 59,
          "goalsDiff": -4,
          "group": "Ligue 1",
          "form": "WWLWD",
          "status": "same",
          "description": null,
          "all": {
            "played": 38,
            "win": 17,
            "draw": 8,
            "lose": 13,
            "goals": {
              "for": 45,
              "against": 49
            }
          },
          "home": {
            "played": 19,
            "win": 9,
            "draw": 3,
            "lose": 7,
            "goals": {
              "for": 20,
              "against": 28
            }
          },
          "away": {
            "played": 19,
            "win": 8,
            "draw": 5,
            "lose": 6,
            "goals": {
              "for": 25,
              "against": 21
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 9,
          "team": {
            "id": 84,
            "name": "Nice",
            "logo": "https://media-2.api-sports.io/football/teams/84.png"
          },
          "points": 58,
          "goalsDiff": 11,
          "group": "Ligue 1",
          "form": "WWDLW",
          "status": "same",
          "description": null,
          "all": {
            "played": 38,
            "win": 15,
            "draw": 13,
            "lose": 10,
            "goals": {
              "for": 48,
              "against": 37
            }
          },
          "home": {
            "played": 19,
            "win": 7,
            "draw": 7,
            "lose": 5,
            "goals": {
              "for": 24,
              "against": 18
            }
          },
          "away": {
            "played": 19,
            "win": 8,
            "draw": 6,
            "lose": 5,
            "goals": {
              "for": 24,
              "against": 19
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 10,
          "team": {
            "id": 97,
            "name": "Lorient",
            "logo": "https://media-3.api-sports.io/football/teams/97.png"
          },
          "points": 55,
          "goalsDiff": -1,
          "group": "Ligue 1",
          "form": "WLLDW",
          "status": "same",
          "description": null,
          "all": {
            "played": 38,
            "win": 15,
            "draw": 10,
            "lose": 13,
            "goals": {
              "for": 52,
              "against": 53
            }
          },
          "home": {
            "played": 19,
            "win": 9,
            "draw": 4,
            "lose": 6,
            "goals": {
              "for": 26,
              "against": 21
            }
          },
          "away": {
            "played": 19,
            "win": 6,
            "draw": 6,
            "lose": 7,
            "goals": {
              "for": 26,
              "against": 32
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 11,
          "team": {
            "id": 93,
            "name": "Reims",
            "logo": "https://media-3.api-sports.io/football/teams/93.png"
          },
          "points": 51,
          "goalsDiff": 0,
          "group": "Ligue 1",
          "form": "LLDLW",
          "status": "same",
          "description": null,
          "all": {
            "played": 38,
            "win": 12,
            "draw": 15,
            "lose": 11,
            "goals": {
              "for": 45,
              "against": 45
            }
          },
          "home": {
            "played": 19,
            "win": 8,
            "draw": 6,
            "lose": 5,
            "goals": {
              "for": 28,
              "against": 23
            }
          },
          "away": {
            "played": 19,
            "win": 4,
            "draw": 9,
            "lose": 6,
            "goals": {
              "for": 17,
              "against": 22
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 12,
          "team": {
            "id": 82,
            "name": "Montpellier",
            "logo": "https://media-3.api-sports.io/football/teams/82.png"
          },
          "points": 50,
          "goalsDiff": 3,
          "group": "Ligue 1",
          "form": "WLWDL",
          "status": "same",
          "description": null,
          "all": {
            "played": 38,
            "win": 15,
            "draw": 5,
            "lose": 18,
            "goals": {
              "for": 65,
              "against": 62
            }
          },
          "home": {
            "played": 19,
            "win": 7,
            "draw": 3,
            "lose": 9,
            "goals": {
              "for": 29,
              "against": 29
            }
          },
          "away": {
            "played": 19,
            "win": 8,
            "draw": 2,
            "lose": 9,
            "goals": {
              "for": 36,
              "against": 33
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 13,
          "team": {
            "id": 96,
            "name": "Toulouse",
            "logo": "https://media-2.api-sports.io/football/teams/96.png"
          },
          "points": 48,
          "goalsDiff": -6,
          "group": "Ligue 1",
          "form": "WDDDD",
          "status": "same",
          "description": "Promotion - Europa League (Group Stage: )",
          "all": {
            "played": 38,
            "win": 13,
            "draw": 9,
            "lose": 16,
            "goals": {
              "for": 51,
              "against": 57
            }
          },
          "home": {
            "played": 19,
            "win": 6,
            "draw": 6,
            "lose": 7,
            "goals": {
              "for": 27,
              "against": 27
            }
          },
          "away": {
            "played": 19,
            "win": 7,
            "draw": 3,
            "lose": 9,
            "goals": {
              "for": 24,
              "against": 30
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 14,
          "team": {
            "id": 106,
            "name": "Stade Brestois 29",
            "logo": "https://media-2.api-sports.io/football/teams/106.png"
          },
          "points": 44,
          "goalsDiff": -10,
          "group": "Ligue 1",
          "form": "LWWWL",
          "status": "same",
          "description": null,
          "all": {
            "played": 38,
            "win": 11,
            "draw": 11,
            "lose": 16,
            "goals": {
              "for": 44,
              "against": 54
            }
          },
          "home": {
            "played": 19,
            "win": 7,
            "draw": 5,
            "lose": 7,
            "goals": {
              "for": 24,
              "against": 26
            }
          },
          "away": {
            "played": 19,
            "win": 4,
            "draw": 6,
            "lose": 9,
            "goals": {
              "for": 20,
              "against": 28
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 15,
          "team": {
            "id": 95,
            "name": "Strasbourg",
            "logo": "https://media-2.api-sports.io/football/teams/95.png"
          },
          "points": 40,
          "goalsDiff": -8,
          "group": "Ligue 1",
          "form": "LDDWW",
          "status": "same",
          "description": null,
          "all": {
            "played": 38,
            "win": 9,
            "draw": 13,
            "lose": 16,
            "goals": {
              "for": 51,
              "against": 59
            }
          },
          "home": {
            "played": 19,
            "win": 5,
            "draw": 7,
            "lose": 7,
            "goals": {
              "for": 25,
              "against": 26
            }
          },
          "away": {
            "played": 19,
            "win": 4,
            "draw": 6,
            "lose": 9,
            "goals": {
              "for": 26,
              "against": 33
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 16,
          "team": {
            "id": 83,
            "name": "Nantes",
            "logo": "https://media-3.api-sports.io/football/teams/83.png"
          },
          "points": 36,
          "goalsDiff": -18,
          "group": "Ligue 1",
          "form": "WLLDL",
          "status": "same",
          "description": null,
          "all": {
            "played": 38,
            "win": 7,
            "draw": 15,
            "lose": 16,
            "goals": {
              "for": 37,
              "against": 55
            }
          },
          "home": {
            "played": 19,
            "win": 5,
            "draw": 8,
            "lose": 6,
            "goals": {
              "for": 20,
              "against": 26
            }
          },
          "away": {
            "played": 19,
            "win": 2,
            "draw": 7,
            "lose": 10,
            "goals": {
              "for": 17,
              "against": 29
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 17,
          "team": {
            "id": 108,
            "name": "Auxerre",
            "logo": "https://media-3.api-sports.io/football/teams/108.png"
          },
          "points": 35,
          "goalsDiff": -28,
          "group": "Ligue 1",
          "form": "LDLLD",
          "status": "same",
          "description": "Relegation - Ligue 2",
          "all": {
            "played": 38,
            "win": 8,
            "draw": 11,
            "lose": 19,
            "goals": {
              "for": 35,
              "against": 63
            }
          },
          "home": {
            "played": 19,
            "win": 5,
            "draw": 7,
            "lose": 7,
            "goals": {
              "for": 18,
              "against": 28
            }
          },
          "away": {
            "played": 19,
            "win": 3,
            "draw": 4,
            "lose": 12,
            "goals": {
              "for": 17,
              "against": 35
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 18,
          "team": {
            "id": 98,
            "name": "Ajaccio",
            "logo": "https://media-1.api-sports.io/football/teams/98.png"
          },
          "points": 26,
          "goalsDiff": -51,
          "group": "Ligue 1",
          "form": "WLLLD",
          "status": "same",
          "description": "Relegation - Ligue 2",
          "all": {
            "played": 38,
            "win": 7,
            "draw": 5,
            "lose": 26,
            "goals": {
              "for": 23,
              "against": 74
            }
          },
          "home": {
            "played": 19,
            "win": 4,
            "draw": 3,
            "lose": 12,
            "goals": {
              "for": 10,
              "against": 30
            }
          },
          "away": {
            "played": 19,
            "win": 3,
            "draw": 2,
            "lose": 14,
            "goals": {
              "for": 13,
              "against": 44
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 19,
          "team": {
            "id": 110,
            "name": "Estac Troyes",
            "logo": "https://media-3.api-sports.io/football/teams/110.png"
          },
          "points": 24,
          "goalsDiff": -36,
          "group": "Ligue 1",
          "form": "DLDLL",
          "status": "same",
          "description": "Relegation - Ligue 2",
          "all": {
            "played": 38,
            "win": 4,
            "draw": 12,
            "lose": 22,
            "goals": {
              "for": 45,
              "against": 81
            }
          },
          "home": {
            "played": 19,
            "win": 1,
            "draw": 11,
            "lose": 7,
            "goals": {
              "for": 19,
              "against": 30
            }
          },
          "away": {
            "played": 19,
            "win": 3,
            "draw": 1,
            "lose": 15,
            "goals": {
              "for": 26,
              "against": 51
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        },
        {
          "rank": 20,
          "team": {
            "id": 77,
            "name": "Angers",
            "logo": "https://media-2.api-sports.io/football/teams/77.png"
          },
          "points": 18,
          "goalsDiff": -48,
          "group": "Ligue 1",
          "form": "LWDLL",
          "status": "same",
          "description": "Relegation - Ligue 2",
          "all": {
            "played": 38,
            "win": 4,
            "draw": 6,
            "lose": 28,
            "goals": {
              "for": 33,
              "against": 81
            }
          },
          "home": {
            "played": 19,
            "win": 3,
            "draw": 3,
            "lose": 13,
            "goals": {
              "for": 20,
              "against": 36
            }
          },
          "away": {
            "played": 19,
            "win": 1,
            "draw": 3,
            "lose": 15,
            "goals": {
              "for": 13,
              "against": 45
            }
          },
          "update": "2023-06-03T00:00:00+00:00"
        }
      ]
    ]
  }
}

Créons un composant appelé Standing pour afficher le standing, le logo et toutes les informations provenant de l'API.

import React from "react"

function Standing({ leagueInfos }) {
  console.log(leagueInfos)
  return <div className="container mx-auto px-4 sm:px-8"></div>
}

export default Standing

Nous allons maintenant créer un tableau que nous utiliserons pour afficher ce titre de colonne, nous y mettrons le classement, l'équipe, les victoires, les défaites, les matchs nuls... Nous allons créer une carte de ce tableau et l'afficher dans une balise < th >.

import React from "react"

function Standing({ leagueInfos }) {
  const data = [
    "Ranking",
    "Team",
    "GP",
    "W",
    "D",
    "L",
    "GF",
    "GA",
    "GD",
    "PTS",
    "Shape",
  ]

  const filteredData = data.map((title, index) => (
    <th
      key={index}
      className="border-red-20 w-auto border-b-2 bg-green-200 px-5 py-3 text-left text-xs font-semibold uppercase text-gray-600 "
    >
      {title}
    </th>
  ))

  return <div className="container mx-auto px-4 sm:px-8"></div>
}

export default Standing
  • Rang : Le rang du club dans le classement.
  • Équipe : Le nom du club.
  • Joué : Nombre de matchs joués par le club.
  • Victoire : Le nombre de matchs gagnés par le club.
  • Match nul : Le nombre de matchs nuls du club.
  • Perdre : Le nombre de matchs que le club a perdus.
  • Buts pour : Le nombre de buts marqués par le club.
  • Buts contre : Nombre de buts encaissés par le club.
  • Différence de buts : La différence entre les buts marqués et les buts encaissés par le club.
  • Points : Le nombre de points accumulés par le club.
  • Forme : La forme récente du club (par exemple, "LDWWW").

Nous allons maintenant créer chaque ligne de notre classement avec les informations relatives à chaque club, en stockant les lignes du tableau dans un const clubs.

import React from "react"

function Standing({ leagueInfos }) {
  const data = [
    "Ranking",
    "Team",
    "GP",
    "W",
    "D",
    "L",
    "GF",
    "GA",
    "GD",
    "PTS",
    "Shape",
  ]

  const filteredData = data.map((title, index) => (
    <th
      key={index}
      className="border-red-20 w-auto border-b-2 bg-green-200 px-5 py-3 text-left text-xs font-semibold uppercase text-gray-600 "
    >
      {title}
    </th>
  ))

  const clubs = leagueInfos.standings[0].map((club) => (
    <tr key={club.rank}>
      <td className="w-32 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        <div className="flex items-center">
          <div className="flex-shrink-0 ">
            <p>{club.rank}</p>
          </div>
        </div>
      </td>
      <td className="w-36 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        <Image
          width={40}
          height={40}
          className="h-10  w-10 rounded-full"
          src={club.team.logo}
          alt=""
        />
      </td>
      <td className="w-36 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        <a href="#">{club.team.name}</a>
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.played}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.win}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.draw}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.lose}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.goals.for}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.goals.against}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.goalsDiff}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.points}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.form}
      </td>
    </tr>
  ))

  return <div className="container mx-auto px-4 sm:px-8"></div>
}

export default Standing

La dernière étape consiste maintenant à afficher ces informations à l'écran en les passant dans notre div vide

import Image from "next/image"
import React from "react"

function Standing({ leagueInfos }) {
  const data = [
    "Ranking",
    "Team",
    "GP",
    "W",
    "D",
    "L",
    "GF",
    "GA",
    "GD",
    "PTS",
    "Shape",
  ]

  const filteredData = data.map((title, index) => (
    <th
      key={index}
      className="border-red-20 w-auto border-b-2 bg-green-200 px-5 py-3 text-left text-xs font-semibold uppercase text-gray-600 "
    >
      {title}
    </th>
  ))

  const clubs = leagueInfos.standings[0].map((club) => (
    <tr key={club.rank}>
      <td className="w-32 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        <div className="flex items-center">
          <div className="flex-shrink-0 ">
            <p>{club.rank}</p>
          </div>
        </div>
      </td>
      <td className="w-36 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        <Image
          width={40}
          height={40}
          className="h-10  w-10 rounded-full"
          src={club.team.logo}
          alt=""
        />
      </td>
      <td className="w-36 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        <a href="#">{club.team.name}</a>
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.played}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.win}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.draw}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.lose}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.goals.for}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.all.goals.against}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.goalsDiff}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.points}
      </td>
      <td className="w-10 border-b border-gray-200 bg-white px-2 py-2 text-sm">
        {club.form}
      </td>
    </tr>
  ))

  return (
    <div className="container mx-auto px-4 sm:px-8">
      <div className="py-8">
        <div className="flex space-x-4 space-y-6">
          <Image
            height={100}
            width={100}
            className=" h-24"
            src={leagueInfos.logo}
            alt={leagueInfos.name}
          />
          <h2 className="text-xl font-semibold leading-tight">
            {leagueInfos.name}, {leagueInfos.country}
          </h2>
          <Image
            height={30}
            width={30}
            className=" h-7"
            src={leagueInfos.flag}
            alt={leagueInfos.name}
          />
        </div>
        <div className="-mx-4 w-full overflow-x-scroll px-4 py-4 sm:-mx-8 sm:px-8 xl:overflow-x-hidden">
          <div className="inline-block min-w-full rounded-lg shadow ">
            <table className="min-w-full leading-normal">
              <thead>
                <tr>{filteredData}</tr>
              </thead>
              <tbody>{clubs}</tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  )
}

export default Standing

Avant de continuer, nous devons autoriser Next JS à utiliser l'image de notre API, sinon nous aurons une erreur, nous allons donc modifier le fichier next.config.js

/** @type {import('next').NextConfig} */

module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "media-2.api-sports.io",
      },
      {
        protocol: "https",
        hostname: "media-3.api-sports.io",
      },
      {
        protocol: "https",
        hostname: "media-1.api-sports.io",
      },
    ],
  },
}

Pour voir les classements à l'écran, nous devons importer les classements dans notre application/[leagueID] et passer les leagueInfos en tant que Props.

import Standing from "@/components/Standing"
import React from "react"

async function getData(params) {
  const url = `https://api-football-v1.p.rapidapi.com/v3/standings?season=2022&league=${params}`
  const options = {
    method: "GET",
    headers: {
      "X-RapidAPI-Key": "YOUR-API-KEY",
      "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
    },
  }

  const res = await fetch(url, options)
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.

  // Recommendation: handle errors
  if (!res.ok) {
    // This will activate the closest `error.js` Error Boundary
    throw new Error("Failed to fetch data")
  }

  return res.json()
}

async function Page({ params }) {
  const data = await getData(params?.leagueID)

  const leagueInfos = data?.response[0]?.league

  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <Standing leagueInfos={leagueInfos} />
    </main>
  )
}

export default Page

Nous sommes ici en train d'utiliser une API pour créer nos classements de football, et nous avons vu quelques méthodes Next JS.

Conclusion

Avec Next.js et API-Football, nous avons construit une application de classement de football puissante qui rapproche les fans de l'action sur et en dehors du terrain. En suivant les étapes décrites dans cet article de blog, vous pouvez créer une application riche en fonctionnalités qui maintient les fans informés, engagés et immergés dans le monde des classements de football.

Lien vers le répertoire Github

Github Link

Share on TwitterShare on Linkedin