Introduction
Dans cet article, nous allons voir comment intégrer l'API Spotify dans une application NextJS. Nous couvrirons les étapes nécessaires pour s'authentifier, se connecter et récupérer les données de Spotify afin d'enrichir votre projet NextJS de fonctionnalités musicales.
Prérequis
Avant de commencer, assurez-vous d'avoir :
- Une connaissance de base de JavaScript et de React.
- Node.js installé sur votre machine.
- Un compte Spotify Developer et une application enregistrée pour obtenir vos clés d'API.
Étape 1 : Création du projet NextJS
Créez un nouveau projet NextJS si ce n'est pas déjà fait :
npx create-next-app spotify-nextjs-app
cd spotify-nextjs-app
Lorsque l'assistant vous le propose, choisissez TypeScript, Tailwind CSS et l'App Router. La suite de ce guide suppose ces choix par défaut, avec l'alias d'import @/*.
Étape 2 : Enregistrement de votre application Spotify
- Ouvrez le Spotify Developer Dashboard et cliquez sur Create app.
- Donnez-lui un nom et une description, et ajoutez
http://127.0.0.1:3000/callbackcomme Redirect URI. Spotify n'accepte plus les URI de redirection enlocalhost: utilisez l'adresse de bouclage. - Sélectionnez Web API, enregistrez, puis copiez le Client ID et le Client secret depuis les paramètres de l'application.
Étape 3 : Obtention d'un refresh token
Votre site affiche votre propre activité d'écoute : les visiteurs ne se connectent donc jamais. À la place, vous autorisez votre application une seule fois et conservez le refresh token obtenu sur le serveur, où il est échangé contre des jetons d'accès de courte durée.
Ouvrez cette URL dans votre navigateur, en remplaçant YOUR_CLIENT_ID :
https://accounts.spotify.com/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=http%3A%2F%2F127.0.0.1%3A3000%2Fcallback&scope=user-read-currently-playing
Après avoir accepté, Spotify vous redirige vers http://127.0.0.1:3000/callback?code=.... La page elle-même ne se charge pas, c'est normal : copiez la valeur du paramètre code depuis la barre d'adresse et échangez-la aussitôt (les codes expirent vite) :
curl -X POST https://accounts.spotify.com/api/token \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code=THE_CODE \
--data-urlencode redirect_uri=http://127.0.0.1:3000/callback
La réponse JSON contient un refresh_token. Enregistrez les trois valeurs dans un fichier .env.local à la racine de votre projet :
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
SPOTIFY_REFRESH_TOKEN=your_refresh_token
Ne préfixez jamais ces variables par NEXT_PUBLIC_ : vos secrets seraient alors envoyés au navigateur de chaque visiteur. Si vous révoquez l'accès de l'application dans les paramètres de votre compte Spotify, le refresh token cesse de fonctionner et vous devez refaire cette étape.
Étape 4 : Appel de l'API Spotify depuis le serveur
Créez lib/spotify.ts. Il échange le refresh token contre un jeton d'accès, puis demande à Spotify ce que vous écoutez en ce moment :
const TOKEN_URL = "https://accounts.spotify.com/api/token";
const NOW_PLAYING_URL = "https://api.spotify.com/v1/me/player/currently-playing";
export type NowPlaying =
| { isPlaying: false }
| { isPlaying: true; title: string; artist: string; albumImageUrl: string; songUrl: string };
async function getAccessToken(): Promise<string> {
const credentials = Buffer.from(
`${process.env.SPOTIFY_CLIENT_ID}:${process.env.SPOTIFY_CLIENT_SECRET}`,
).toString("base64");
const response = await fetch(TOKEN_URL, {
method: "POST",
headers: {
Authorization: `Basic ${credentials}`,
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: process.env.SPOTIFY_REFRESH_TOKEN ?? "",
}),
cache: "no-store",
});
if (!response.ok) throw new Error(`Spotify token request failed with ${response.status}`);
const data: { access_token: string } = await response.json();
return data.access_token;
}
export async function getNowPlaying(): Promise<NowPlaying> {
const response = await fetch(NOW_PLAYING_URL, {
headers: { Authorization: `Bearer ${await getAccessToken()}` },
cache: "no-store",
});
// 204 No Content : rien n'est en cours de lecture.
if (response.status === 204) return { isPlaying: false };
if (!response.ok) throw new Error(`Spotify request failed with ${response.status}`);
const song = await response.json();
if (!song.is_playing || !song.item) return { isPlaying: false };
return {
isPlaying: true,
title: song.item.name,
artist: song.item.artists.map((artist: { name: string }) => artist.name).join(", "),
albumImageUrl: song.item.album.images[0]?.url ?? "",
songUrl: song.item.external_urls.spotify,
};
}
Étape 5 : Exposition des données avec un Route Handler
Le navigateur ne doit jamais voir vos identifiants : il interroge donc votre propre route d'API plutôt que Spotify. Créez app/api/now-playing/route.ts :
import { NextResponse } from "next/server";
import { getNowPlaying } from "@/lib/spotify";
export async function GET() {
try {
return NextResponse.json(await getNowPlaying());
} catch (error) {
console.error(error);
return NextResponse.json({ error: "Spotify request failed" }, { status: 502 });
}
}
Lancez le serveur de développement avec npm run dev et ouvrez http://localhost:3000/api/now-playing : vous devriez voir {"isPlaying":false}, ou le titre que vous écoutez.
Étape 6 : Affichage du titre avec SWR
Installez SWR pour interroger la route depuis le navigateur et la rafraîchir automatiquement :
npm install swr
Les pochettes d'album sont servies depuis i.scdn.co : autorisez cet hôte pour next/image dans next.config.ts :
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
images: {
remotePatterns: [{ protocol: "https", hostname: "i.scdn.co" }],
},
};
export default nextConfig;
Créez ensuite le widget dans components/NowPlaying.tsx :
"use client";
import Image from "next/image";
import useSWR from "swr";
import type { NowPlaying as NowPlayingData } from "@/lib/spotify";
const fetcher = (url: string) => fetch(url).then((res) => res.json());
export function NowPlaying() {
const { data } = useSWR<NowPlayingData>("/api/now-playing", fetcher, {
refreshInterval: 10_000,
});
if (!data?.isPlaying) {
return <p className="text-sm text-gray-400">Not playing anything right now</p>;
}
return (
<a
href={data.songUrl}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-4 rounded-xl bg-white/5 p-3 hover:bg-white/10"
>
<Image src={data.albumImageUrl} alt="" width={64} height={64} className="rounded-md" />
<div className="min-w-0">
<p className="truncate font-semibold">{data.title}</p>
<p className="truncate text-sm text-gray-400">{data.artist}</p>
</div>
</a>
);
}
Importer uniquement le type NowPlaying depuis lib/spotify.ts ne pose aucun problème : les types disparaissent à la compilation, donc aucun code serveur n'atteint le navigateur. Enfin, affichez le widget où vous voulez, par exemple dans app/page.tsx :
import { NowPlaying } from "@/components/NowPlaying";
export default function Home() {
return (
<main className="mx-auto max-w-md p-8">
<NowPlaying />
</main>
);
}
Lancez un titre sur Spotify : le widget se met à jour en moins de dix secondes.
Pour aller plus loin
Le même jeton d'accès ouvre de nombreux autres endpoints. Ajoutez le scope correspondant à l'étape 3 (et générez un nouveau refresh token), puis appelez-les depuis lib/spotify.ts :
GET /v1/me/player/recently-playedavec le scopeuser-read-recently-playedGET /v1/me/top/tracksetGET /v1/me/top/artistsavec le scopeuser-top-read
Chaque nouvelle requête récupère un nouveau jeton d'accès. Les jetons d'accès restent valides une heure : sur un site très fréquenté, mieux vaut garder le jeton en mémoire jusqu'à son expiration.
Conclusion
Vous disposez maintenant d'une application NextJS qui lit votre activité Spotify côté serveur et l'affiche en direct avec SWR, sans jamais exposer vos identifiants au navigateur. Vous pouvez désormais construire une page complète « ce que j'écoute » avec vos titres et artistes préférés.
