Quand utiliser les routes API
Utilisez les routes API quand vous avez besoin de :
- Secrets côté serveur — Clés API, identifiants de base de données ou tokens qui ne doivent jamais atteindre le client
- Opérations de base de données — Requêtes directes à la base de données qui ne doivent pas être exposées
- Proxies d'API tiers — Masquer les clés API lors de l'appel de services externes (OpenAI, Stripe, etc.)
- Validation côté serveur — Valider les données avant l'écriture en base
- Endpoints de webhooks — Recevoir des callbacks de services comme Stripe ou GitHub
- Limitation de débit — Contrôler l'accès au niveau serveur
- Calculs intensifs — Décharger le traitement qui serait lent sur mobile
Quand NE PAS utiliser les routes API
Évitez les routes API quand :
- Les données sont déjà publiques — Utilisez fetch direct vers des APIs publiques
- Aucun secret requis — Données statiques ou opérations sûres côté client
- Mises à jour en temps réel nécessaires — Utilisez WebSockets ou des services comme Supabase Realtime
- Simple CRUD — Envisagez Firebase, Supabase ou Convex pour les backends gérés
- Uploads de fichiers — Utilisez les uploads directs au stockage (URLs présignées S3, Cloudflare R2)
- Authentification uniquement — Utilisez Clerk, Auth0 ou Firebase Auth
Structure des fichiers
Les routes API se trouvent dans le répertoire app avec le suffixe +api.ts :
app/
api/
hello+api.ts → GET /api/hello
users+api.ts → /api/users
users/[id]+api.ts → /api/users/:id
(tabs)/
index.tsx
Route API basique
// app/api/hello+api.ts
export function GET(request: Request) {
return Response.json({ message: "Hello from Expo!" });
}
Méthodes HTTP
Exportez des fonctions nommées pour chaque méthode HTTP :
// app/api/items+api.ts
export function GET(request: Request) {
return Response.json({ items: [] });
}
export async function POST(request: Request) {
const body = await request.json();
return Response.json({ created: body }, { status: 201 });
}
export async function PUT(request: Request) {
const body = await request.json();
return Response.json({ updated: body });
}
export async function DELETE(request: Request) {
return new Response(null, { status: 204 });
}
Routes dynamiques
// app/api/users/[id]+api.ts
export function GET(request: Request, { id }: { id: string }) {
return Response.json({ userId: id });
}
Gestion des requêtes
Paramètres de requête
export function GET(request: Request) {
const url = new URL(request.url);
const page = url.searchParams.get("page") ?? "1";
const limit = url.searchParams.get("limit") ?? "10";
return Response.json({ page, limit });
}
En-têtes
export function GET(request: Request) {
const auth = request.headers.get("Authorization");
if (!auth) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
return Response.json({ authenticated: true });
}
Corps JSON
export async function POST(request: Request) {
const { email, password } = await request.json();
if (!email || !password) {
return Response.json({ error: "Missing fields" }, { status: 400 });
}
return Response.json({ success: true });
}
Variables d'environnement
Utilisez process.env pour les secrets côté serveur :
// app/api/ai+api.ts
export async function POST(request: Request) {
const { prompt } = await request.json();
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
body: JSON.stringify({
model: "gpt-4",
messages: [{ role: "user", content: prompt }],
}),
});
const data = await response.json();
return Response.json(data);
}
Définissez les variables d'environnement :
- Local : Créez un fichier
.env(ne commitez jamais) - EAS Hosting : Utilisez
eas env:createou le tableau de bord Expo
En-têtes CORS
Ajoutez CORS pour les clients web :
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
};
export function OPTIONS() {
return new Response(null, { headers: corsHeaders });
}
export function GET() {
return Response.json({ data: "value" }, { headers: corsHeaders });
}
Gestion des erreurs
export async function POST(request: Request) {
try {
const body = await request.json();
// Traiter...
return Response.json({ success: true });
} catch (error) {
console.error("API error:", error);
return Response.json({ error: "Internal server error" }, { status: 500 });
}
}
Test local
Démarrez le serveur de développement avec les routes API :
npx expo serve
Cela démarre un serveur local à http://localhost:8081 avec support complet des routes API.
Testez avec curl :
curl http://localhost:8081/api/hello
curl -X POST http://localhost:8081/api/users -H "Content-Type: application/json" -d '{"name":"Test"}'
Déploiement sur EAS Hosting
Prérequis
npm install -g eas-cli
eas login
Déployer
eas deploy
Cela construit et déploie vos routes API sur EAS Hosting (Cloudflare Workers).
Variables d'environnement pour la production
# Créer un secret
eas env:create --name OPENAI_API_KEY --value sk-xxx --environment production
# Ou utilisez le tableau de bord Expo
Domaine personnalisé
Configurez dans eas.json ou le tableau de bord Expo.
Runtime EAS Hosting (Cloudflare Workers)
Les routes API s'exécutent sur Cloudflare Workers. Limitations clés :
APIs manquantes ou limitées
- Pas de système de fichiers Node.js — Module
fsindisponible - Pas de modules Node natifs — Utilisez les Web APIs ou polyfills
- Temps d'exécution limité — Timeout de 30 secondes pour les tâches CPU-intensives
- Pas de connexions persistantes — WebSockets nécessite Durable Objects
- fetch est disponible — Utilisez fetch standard pour les requêtes HTTP
Utilisez les Web APIs à la place
// Utilisez Web Crypto au lieu de crypto Node
const hash = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode("data")
);
// Utilisez fetch au lieu de node-fetch
const response = await fetch("https://api.example.com");
// Utilisez Response/Request (déjà disponibles)
return new Response(JSON.stringify(data), {
headers: { "Content-Type": "application/json" },
});
Options de base de données
Comme le système de fichiers est indisponible, utilisez les bases de données cloud :
- Cloudflare D1 — SQLite à la périphérie
- Turso — SQLite distribué
- PlanetScale — MySQL serverless
- Supabase — Postgres avec API REST
- Neon — Postgres serverless
Exemple avec Turso :
// app/api/users+api.ts
import { createClient } from "@libsql/client/web";
const db = createClient({
url: process.env.TURSO_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
});
export async function GET() {
const result = await db.execute("SELECT * FROM users");
return Response.json(result.rows);
}
Appeler les routes API depuis le client
// Depuis les composants React Native
const response = await fetch("/api/hello");
const data = await response.json();
// Avec body
const response = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "John" }),
});
Motifs courants
Middleware d'authentification
// utils/auth.ts
export async function requireAuth(request: Request) {
const token = request.headers.get("Authorization")?.replace("Bearer ", "");
if (!token) {
throw new Response(JSON.stringify({ error: "Unauthorized" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
// Vérifier le token...
return { userId: "123" };
}
// app/api/protected+api.ts
import { requireAuth } from "../../utils/auth";
export async function GET(request: Request) {
const { userId } = await requireAuth(request);
return Response.json({ userId });
}
Proxy d'API externe
// app/api/weather+api.ts
export async function GET(request: Request) {
const url = new URL(request.url);
const city = url.searchParams.get("city");
const response = await fetch(
`https://api.weather.com/v1/current?city=${city}&key=${process.env.WEATHER_API_KEY}`
);
return Response.json(await response.json());
}
Règles
- NE JAMAIS exposer les clés API ou secrets dans le code client
- TOUJOURS valider et assainir les entrées utilisateur
- Utilisez les codes de statut HTTP appropriés (200, 201, 400, 401, 404, 500)
- Gérez les erreurs gracieusement avec try/catch
- Maintenez les routes API ciblées — une responsabilité par endpoint
- Utilisez TypeScript pour la sécurité des types
- Enregistrez les erreurs côté serveur pour le débogage