Utilise Zod pour typer et sécuriser les réponses de l’API.
Renseigne ton email pour débloquer immédiatement cette formation gratuite.
Dans cette leçon, tu vas apprendre à protéger tes données provenant de l’API The Movie Database en utilisant Zod. Plutôt que de travailler avec du any
, Zod te permet de :
ℹ️ On se concentre ici sur la validation côté serveur dans
app/server/movies.ts
.
Démarre par ajouter Zod à ton projet :
1npm install zod
Ouvre app/server/movies.ts
et ajoute un schéma qui reflète la structure renvoyée par l’API :
1import { z } from 'zod'23const GetMoviesSchema = z.object({ // ^? z.object: crée un schema objet4page: z.number(), // ^? z.number: nombre de page5results: z.array(6z.object({7id: z.number(), // ^? z.number: identifiant du film8title: z.string(), // ^? z.string: titre du film9poster_path: z.string(), // ^? z.string: chemin de l'affiche10release_date: z.string(), // ^? z.string: date de sortie11})12),13})
Pourquoi ce schéma ?
On décrit ici exactement ce qu’on attend de l’API. Si la réponse ne correspond pas,
GetMoviesSchema.parse()
lève une exception, ce qui évite des bugs silencieux.
Ensuite, utilise parse()
pour valider la réponse brute et extraire results
:
1export async function getMovies() {2const data = await fetchMovieDb({ url: '/movie/popular' })3// @callout: parse l'objet `data` selon le schéma et lève si invalidé4const { results } = GetMoviesSchema.parse(data)5return results6}
Une fois validées, les données sont considérées « sûres ». Tu peux boucler dessus sans crainte.
Movie
Pour bénéficier de la complétion TypeScript, crée un type à partir du schéma :
1export type Movie = z.infer<2typeof GetMoviesSchema3>['results'][number] // ^? infère le type d'un élément du tableau results
Désormais, getMovies()
renvoie un Movie[]
et chaque movie
est typé précisément.
L’API propose également /tv/popular
. Tu peux définir un schéma similaire :
1const GetSeriesSchema = z.object({2page: z.number(),3results: z.array(4z.object({5id: z.number(),6name: z.string(),7poster_path: z.string(),8first_air_date: z.string(),9})10),11})1213export async function getSeries() {14const data = await fetchMovieDb({ url: '/tv/popular' })15const { results } = GetSeriesSchema.parse(data)16return results17}1819export type Series = z.infer<20typeof GetSeriesSchema21>['results'][number]
Tip
Toujours créer un schéma distinct si la forme des données change (ex.
name
vstitle
).
Pour l’appel /movie/{id}
ou /tv/{id}
, tu peux valider un objet unique :
1// film détaillé2const GetMovieDetailSchema = z.object({3id: z.number(),4title: z.string(),5poster_path: z.string(),6release_date: z.string(),7})89export async function getMovie({ movieId }: { movieId: number }) {10'use cache'11const data = await fetchMovieDb({ url: `/movie/${movieId}` })12const result = GetMovieDetailSchema.parse(data) // lève si champs manquant13return result14}
Tu peux faire de même pour getSerie()
.
safeParse()
pour récupérer un objet success
/error
si tu veux gérer les erreurs sans exception.Valider de gros objets peut être coûteux. Si tu appelles souvent le même endpoint,
cache le résultat ou utilise fetch
avec use cache
pour React Server Components.
Créer un schéma de détail de série
Réalise un GetSerieDetailSchema
dans app/server/movies.ts
et valide la réponse de /tv/{serieId}
.
Tester la validation côté serveur
Modifie getMovies()
pour utiliser safeParse()
. En cas d’erreur, logue le détail avant de rethrow.
Étendre le type Movie
Ajoute la propriété overview: z.string()
dans GetMoviesSchema
et vérifie que l’IDE remonte bien le type dans MovieCard
.
Tu as maintenant toutes les clés pour sécuriser tes données externes et tirer pleinement parti de TypeScript !