Cacher une clé d'API
Ce que tu vas apprendre
- Pourquoi une clé placée dans le navigateur est une clé perdue.
- Déclarer et lire un secret avec
runtimeConfig. - Faire passer l'appel par ta propre route, qui joue le rôle d'intermédiaire.
Le problème
Une clé d'API est un mot de passe qui identifie ton application auprès d'un service, souvent payant ou limité. Imagine une API de photos de plats qui demande une clé dans l'en-tête Authorization.
La tentation : appeler directement le service depuis ta page.
<script setup lang="ts">
// MAUVAIS : cette clé part dans le navigateur de chaque visiteur
const { data } = await useFetch('https://api.exemple-photos.test/plats', {
headers: { Authorization: 'Bearer ma-cle-secrete' }
})
</script>
Tout ce qui s'exécute dans le navigateur est lisible : code envoyé, onglet Réseau, en-têtes de requête. N'importe quel visiteur copie la clé et l'utilise à tes frais.
La solution : le serveur appelle, pas le navigateur
Le code de server/ ne quitte jamais le serveur. L'idée : ton navigateur appelle ta route (/api/photos), et c'est cette route qui appelle le service avec la clé. On parle d'intermédiaire (ou proxy).
- Le navigateur envoie
GET /api/photosà ton serveur. - Ta route ajoute la clé et appelle le service externe.
- Elle renvoie la réponse au navigateur, sans la clé.
Étape 1 : déclarer le secret
Dans nuxt.config.ts, une clé hors de public reste côté serveur :
export default defineNuxtConfig({
runtimeConfig: {
cleExemple: '', // secrète : serveur seulement
public: {
nomSite: 'Mes recettes' // publique : visible partout
}
}
})
La valeur réelle va dans .env. Le nom suit la règle de la documentation : majuscules, préfixe NUXT_, mots séparés par _ aux changements de casse. cleExemple devient NUXT_CLE_EXEMPLE.
NUXT_CLE_EXEMPLE=la-vraie-cle
runtimeConfig, sinon elle est ignorée. Et n'utilise jamais NUXT_PUBLIC_ pour un secret : tout ce qui est public est envoyé au navigateur.Étape 2 : la route intermédiaire
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event)
return await $fetch('https://api.exemple-photos.test/plats', {
headers: { Authorization: `Bearer ${config.cleExemple}` }
})
})
useRuntimeConfig(event) reçoit l'event : la documentation le recommande dans les routes serveur pour appliquer les variables d'environnement au moment de l'exécution. Le service reçoit la clé, ton visiteur jamais.
Étape 3 : la page appelle ta route
<script setup lang="ts">
const { data: photos } = await useFetch('/api/photos')
</script>
Dans l'onglet Réseau, tu ne vois que /api/photos. Aucune clé, aucune adresse du service externe.
Aller plus loin avec la route
L'intermédiaire apporte plus que le secret. Tu peux :
- filtrer : ne renvoyer que les champs utiles, comme avec
transform; - valider : n'accepter que les paramètres que tu attends, au lieu de les relayer tels quels ;
- protéger : refuser les requêtes non autorisées avant de dépenser le quota du service.
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event)
const { plat } = getQuery(event)
if (typeof plat !== 'string' || plat.length > 50) {
throw createError({ status: 400, statusText: 'Parametre plat invalide' })
}
return await $fetch('https://api.exemple-photos.test/plats', {
headers: { Authorization: `Bearer ${config.cleExemple}` },
query: { nom: plat }
})
})
Ne relaie jamais une entrée du visiteur sans la vérifier : le chapitre 5 détaille la validation.
Et si la clé a fuité ?
- Considère-la comme volée : révoque-la chez le service, génère-en une nouvelle.
- Vérifie que
.envest dans.gitignoreavant ton premier commit. - Utilise une clé différente par environnement (développement, production).
À retenir
- Tout ce qui s'exécute dans le navigateur est lisible : une clé d'API n'y a jamais sa place.
- Déclare le secret dans
runtimeConfighors depublic, sa valeur dans.envsous le nomNUXT_.... - Ta route
server/api/appelle le service avec la clé ; la page appelle ta route. - Lis la config avec
useRuntimeConfig(event)dans une route serveur. - Une clé qui a fuité se révoque et se remplace, on ne la « cache » pas après coup.
Pour aller plus loin
Sources officielles
Ensuite
Consommer une API externe
Appeler une API publique depuis Nuxt, transformer sa réponse pour ne garder que l'utile et gérer les erreurs et les paramètres.
Écrire une API complète
Construire les cinq routes d'une API de recettes (lire, créer, modifier, supprimer) avec validation des entrées et erreurs claires.
- Projet
- Lignes de code
- Niveau
- Sous-sol
- Appartement
- API dans Nuxt
- Pièce
- 4/7
- Plan n°
- api/04.cacher-une-cle-d-api