Next.js e-commerce : guide complet panier et paiement Stripe

Vingt-septième relecture de la config Stripe Webhook : des utilisateurs disent que l’argent est débité mais la commande reste « en attente de paiement ». En test tout roule — pourquoi ça casse en prod ?
Ma première boutique Next.js, je pensais que le dur serait l’UI et le CSS. En pratique : state du panier, intégration paiement, flux commande — chaque étape est un piège. Redux trop lourd, Context API critiqué sur les perfs, doc Stripe en anglais, et le Webhook ? Mystère total.
Pire : la plupart des tutos ne couvrent que le panier ou le paiement, rarement la chaîne complète. « Quelle lib de state ? », « À quoi sert le Webhook ? », « Comment aligner statut commande et paiement ? »
Cet article vise à combler ces trous. Zustand pour le panier (léger), Stripe pour le paiement, Webhook pour les commandes (seule voie fiable). Code complet à chaque étape, copiable tel quel.
Vous connaissez ce soulagement quand tout passe au vert d’un coup ? En suivant ce guide, vous l’aurez.
Pourquoi Zustand pour le panier ?
State management en 2025 : arrêtez de tergiverser
Franchement, le choix de lib fatigue. Redux = gros livre, Context = articles sur les perfs, Zustand = « trop neuf ». J’ai oscillé entre les trois jusqu’à voir les chiffres.
Depuis 2021, Zustand est parmi les libs React state les plus suivies. En 2025, le modèle tient : fonctionnel, hooks, API courte. Courbe d’apprentissage douce — pas action/reducer/dispatch/middleware comme Redux.
Règle simple :
- Petit (< 10 pages) : Context API, pas de surcouche
- Moyen (10-50) : Zustand
- Grand (50+, plusieurs équipes) : Redux Toolkit
Un panier coche trois cases : partage entre composants (liste, icône, checkout), persistance (refresh), perfs (re-render ciblé). Zustand gère tout ça avec moins de code que Redux.
Code panier Zustand
D’abord les deps :
npm install zustand
Store (/store/cartStore.js) :
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
export const useCartStore = create(
persist(
(set, get) => ({
// État
items: [], // [{ id, name, price, quantity, image }]
// Propriétés calculées
get total() {
return get().items.reduce((sum, item) => sum + item.price * item.quantity, 0)
},
get count() {
return get().items.reduce((sum, item) => sum + item.quantity, 0)
},
// Actions
addItem: (product) => set((state) => {
const existing = state.items.find(item => item.id === product.id)
if (existing) {
return {
items: state.items.map(item =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
)
}
} else {
return { items: [...state.items, { ...product, quantity: 1 }] }
}
}),
removeItem: (productId) => set((state) => ({
items: state.items.filter(item => item.id !== productId)
})),
updateQuantity: (productId, quantity) => set((state) => ({
items: state.items.map(item =>
item.id === productId ? { ...item, quantity } : item
)
})),
clearCart: () => set({ items: [] })
}),
{
name: 'shopping-cart', // clé localStorage
}
)
)
items stocke les lignes, total et count sont calculés, les méthodes gèrent le CRUD. persist écrit dans localStorage — pas de perte au refresh.
Dans un composant :
import { useCartStore } from '@/store/cartStore'
function ProductCard({ product }) {
const addItem = useCartStore(state => state.addItem)
return (
<button onClick={() => addItem(product)}>
Ajouter au panier
</button>
)
}
function CartIcon() {
const count = useCartStore(state => state.count)
return <div>Panier ({count})</div>
}
useCartStore(state => state.addItem) = sélecteur : souscription à addItem seulement, pas de re-render si le reste du panier change. C’est le secret des perfs Zustand.
Déjà sur Redux ? Pas besoin de migrer. Redux Toolkit + RTK Query (ex. projet C-Shopping) trace bien le flux. Pour un nouveau projet, je penche Zustand : moins de friction, livraison plus rapide.
Intégration Stripe de bout en bout
Comprendre le flux Stripe
Première lecture de la doc : Checkout Session, Payment Intent, redirection… « Pourquoi quitter mon site ? »
En fait c’est linéaire :
- Frontend : clic « Payer » → appel API pour créer une Checkout Session
- Backend : Session créée →
session.id - Frontend :
session.id→ Stripe.js → page de paiement hébergée - Utilisateur : saisie carte, validation
- Stripe : succès → Webhook vers votre backend
- Backend : Webhook → commande, stock, e-mail
- Stripe : redirection vers
success_url
Règle d’or : ne jamais traiter le succès paiement côté frontend. Fermeture d’onglet, réseau, utilisateur qui ne revient pas — seul le Webhook est fiable (détail plus bas).
Créer une Checkout Session Stripe
npm install stripe @stripe/stripe-js
.env.local :
STRIPE_SECRET_KEY=sk_test_xxxxx # backend uniquement, jamais exposé
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxx # signature Webhook
/pages/api/create-checkout.js :
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' })
}
try {
const { items } = req.body
const lineItems = items.map(item => ({
price_data: {
currency: 'usd',
product_data: {
name: item.name,
images: [item.image],
},
unit_amount: Math.round(item.price * 100), // centimes
},
quantity: item.quantity,
}))
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: lineItems,
mode: 'payment',
success_url: `${req.headers.origin}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${req.headers.origin}/cart`,
metadata: {
userId: req.user?.id || 'guest',
},
})
res.status(200).json({ sessionId: session.id })
} catch (err) {
console.error('Échec création Checkout Session:', err)
res.status(500).json({ error: err.message })
}
}
Notes :
unit_amount× 100 (Stripe en centimes : 99,99 $ → 9999){CHECKOUT_SESSION_ID}remplacé par Stripe danssuccess_urlmetadatalisible dans le Webhook (userId, notes, etc.)
Checkout côté frontend
/pages/checkout.js :
import { loadStripe } from '@stripe/stripe-js'
import { useCartStore } from '@/store/cartStore'
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY)
export default function CheckoutPage() {
const { items, total } = useCartStore()
const handleCheckout = async () => {
try {
const response = await fetch('/api/create-checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ items }),
})
const { sessionId } = await response.json()
const stripe = await stripePromise
const { error } = await stripe.redirectToCheckout({ sessionId })
if (error) {
console.error('Échec redirection paiement:', error)
alert(error.message)
}
} catch (err) {
console.error('Échec lancement paiement:', err)
alert('Paiement impossible, réessayez plus tard')
}
}
return (
<div>
<h1>Checkout</h1>
{items.map(item => (
<div key={item.id}>
{item.name} x {item.quantity} = ${item.price * item.quantity}
</div>
))}
<div>Total : ${total}</div>
<button onClick={handleCheckout}>Payer</button>
</div>
)
}
Stripe gère formulaire carte, 3DS, anti-fraude. Personnalisation couleurs/logo/polices possible ; UI 100 % custom = Stripe Elements, plus complexe — pas idéal pour débuter.
Page de succès (affichage seulement)
Après paiement, redirection vers success_url :
// /pages/success.js
import { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
export default function SuccessPage() {
const router = useRouter()
const { session_id } = router.query
const [order, setOrder] = useState(null)
useEffect(() => {
if (session_id) {
fetch(`/api/order?session_id=${session_id}`)
.then(res => res.json())
.then(data => setOrder(data))
}
}, [session_id])
if (!order) return <div>Chargement...</div>
return (
<div>
<h1>Paiement réussi !</h1>
<p>Commande : {order.id}</p>
<p>Montant : ${order.total}</p>
</div>
)
}
Cette page est cosmétique. La commande réelle se crée dans le Webhook — section suivante.
Webhook : commandes et synchronisation d’état
Pourquoi le Webhook est indispensable
Ma première version : « retour sur success = payé », toute la logique métier là. Tests : l’utilisateur ferme l’onglet après paiement — pas de commande, panique.
Doc Stripe : Webhook = seule source fiable pour les commandes.
- Redirection utilisateur : fragile (fermeture, réseau, pas de clic)
- Sécurité : création commande, stock, expédition = backend uniquement
- Recommandation Stripe : métier critique dans le Webhook
En bref : Stripe appelle votre serveur (« paiement OK », « abonnement annulé », etc.) et vous réagissez.
Endpoint Webhook
/pages/api/stripe-webhook.js :
import Stripe from 'stripe'
import { buffer } from 'micro'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET
export const config = {
api: {
bodyParser: false,
},
}
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).send('Method not allowed')
}
const buf = await buffer(req)
const sig = req.headers['stripe-signature']
let event
try {
event = stripe.webhooks.constructEvent(buf, sig, webhookSecret)
} catch (err) {
console.error('Échec vérification signature Webhook:', err.message)
return res.status(400).send(`Webhook Error: ${err.message}`)
}
switch (event.type) {
case 'checkout.session.completed':
await handleCheckoutSessionCompleted(event.data.object)
break
case 'payment_intent.succeeded':
await handlePaymentIntentSucceeded(event.data.object)
break
case 'invoice.payment_failed':
await handleInvoicePaymentFailed(event.data.object)
break
default:
console.log(`Événement non géré: ${event.type}`)
}
res.status(200).json({ received: true })
}
async function handleCheckoutSessionCompleted(session) {
console.log('Paiement OK!', session.id)
const userId = session.metadata.userId
const sessionId = session.id
const total = session.amount_total / 100
const existingOrder = await db.order.findUnique({
where: { stripeSessionId: sessionId }
})
if (existingOrder) {
console.log('Commande déjà existante, skip')
return
}
const order = await db.order.create({
data: {
userId,
stripeSessionId: sessionId,
status: 'paid',
total,
}
})
await updateInventory(order.items)
await sendOrderConfirmationEmail(userId, order)
console.log('Commande créée:', order.id)
}
async function handlePaymentIntentSucceeded(paymentIntent) {
console.log('Paiement confirmé:', paymentIntent.id)
}
async function handleInvoicePaymentFailed(invoice) {
console.log('Échec paiement:', invoice.id)
}
Trois impératifs :
- bodyParser: false — corps brut pour la signature
- constructEvent — prouve que l’appel vient de Stripe
- Idempotence —
stripeSessionIdunique évite les doublons si Stripe renvoie l’événement
Tester le Webhook en local
Stripe n’atteint pas localhost sans CLI :
# Mac
brew install stripe/stripe-cli/stripe
# Windows (Scoop)
scoop install stripe
stripe login
stripe listen --forward-to localhost:3000/api/stripe-webhook
Copier le whsec_xxxxx temporaire dans .env.local, puis :
stripe trigger checkout.session.completed
J’ai bloqué des heures sur « signature invalide » : bodyParser encore actif. N’oubliez pas export const config.
Cycle de vie des statuts commande
En attente → Payé → Préparation → Expédié → Terminé
↓
Annulé / Remboursé
Schéma Prisma :
model Order {
id String @id @default(cuid())
stripeSessionId String @unique
userId String
status OrderStatus @default(PENDING)
total Float
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum OrderStatus {
PENDING
PAID
PREPARING
SHIPPED
COMPLETED
CANCELLED
REFUNDED
}
checkout.session.completed → PAID. Expédition et clôture via back-office ou automatisation.
Gestion des erreurs Webhook
async function handleCheckoutSessionCompleted(session) {
try {
// logique métier
} catch (error) {
console.error('Échec traitement commande:', error)
await logError({
type: 'webhook_error',
event: 'checkout.session.completed',
sessionId: session.id,
error: error.message,
})
throw error // Stripe réessaiera
}
}
Échec ? Stripe réessaie 3 jours. Dashboard → Webhooks → historique → Resend manuel possible.
Flux commande complet
Tous les morceaux sont là — voici la chaîne de bout en bout.
Parcours utilisateur
- Fiche produit : « Ajouter au panier » → Zustand, badge +1
- Panier : quantités, « Checkout »
- Checkout : récap, « Payer »
- Frontend : POST
/api/create-checkoutavec items - Backend : Session Stripe,
sessionId - Frontend : redirection page Stripe
- Utilisateur : carte, « Pay »
- Stripe : Webhook →
/api/stripe-webhook - Webhook : signature → commande → stock → e-mail
- Stripe : redirect
/success?session_id=xxx - Frontend : GET
/api/order?session_id=xxxpour l’affichage
L’étape 9 doit tenir dans le Webhook — pas l’étape 11.
Modèle de données
model Order {
id String @id @default(cuid())
stripeSessionId String @unique
userId String
status OrderStatus @default(PENDING)
total Float
items OrderItem[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
}
model OrderItem {
id String @id @default(cuid())
orderId String
productId String
quantity Int
price Float // prix au moment de la commande
order Order @relation(fields: [orderId], references: [id])
product Product @relation(fields: [productId], references: [id])
}
OrderItem.price = prix figé à la commande, pas le prix catalogue actuel — important si les tarifs changent.
Cas limites
1. Stock insuffisant
Avant de créer la Session :
const { items } = req.body
for (const item of items) {
const product = await db.product.findUnique({ where: { id: item.id } })
if (product.stock < item.quantity) {
return res.status(400).json({ error: `Stock insuffisant pour ${product.name}` })
}
}
2. Paiement OK mais Webhook en échec
Retry Stripe 3 jours + Resend Dashboard + job cron pour Sessions payées sans commande.
3. Payé mais pas expédié à temps
Re-vérifier le stock avant expédition ; sinon contact client (remboursement / substitution).
Déploiement production
Le test vert ne suffit pas — quelques pièges en prod.
Variables d’environnement
STRIPE_SECRET_KEY=sk_live_xxxxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxx
Configurer sur Vercel/Netlify. Ne jamais committer la Secret Key.
Endpoint Webhook production
Plus de CLI : Dashboard Stripe
- Stripe Dashboard
- Developers → Webhooks → Add endpoint
- URL :
https://yourdomain.com/api/stripe-webhook - Événements :
checkout.session.completed,payment_intent.succeeded, etc. - Copier le Signing secret dans les variables d’env
Premier déploiement sans ça : commandes fantômes — j’ai perdu une demi-journée avant de voir zéro appel Webhook.
Checklist sécurité
- ✅ Paiement côté backend (frontend = redirection)
- ✅ Signature Webhook (
constructEvent) - ✅ Montant Webhook = montant commande (anti-manipulation)
- ✅ Idempotence
stripeSessionId - ✅ Logs (Sentry, Datadog…)
- ✅ Alertes (échec Webhook, taux de succès)
Re-valider le montant dans le Webhook même si la Session est créée côté serveur — défense en profondeur.
Monitoring
import * as Sentry from '@sentry/nextjs'
export default async function handler(req, res) {
try {
// logique Webhook
} catch (error) {
Sentry.captureException(error, {
tags: {
type: 'stripe_webhook',
event: event.type,
},
})
throw error
}
}
Indicateurs :
- Taux d’échec Webhook (> 5 % → alerte)
- Taux de succès paiement (chute = config ou incident Stripe)
- Durée création commande (> 3 s → investigation)
Synthèse : de zéro à la prod
- Panier : Zustand ou Redux Toolkit selon taille ;
persistpour localStorage - Paiement : Checkout Session + page hébergée Stripe
- Commandes : Webhook — création, stock, e-mails
- Prod : env, endpoint Webhook, monitoring
Trois principes :
- Paiement = backend — le frontend n’est pas de confiance
- Webhook = source de vérité — la redirection utilisateur ne suffit pas
- Sécurité d’abord — signature, idempotence, logs
Première fois ? Environnement test Stripe, carte 4242 4242 4242 4242, puis bascule live.
Ressources :
- Documentation Stripe
- Guide Next.js + Stripe 2025 (Pedro Alonso, « Stripe Next.js 15 complete guide »)
- Projet open source C-Shopping (Redux Toolkit + Stripe)
Moins de pièges, plus de commandes qui passent seules — bon courage.
Implémentation complète panier Next.js et paiement Stripe
Étapes détaillées pour monter panier et paiement de zéro : state management, intégration Stripe et traitement des commandes
⏱️ Estimated time: 2 hr
- 1
Step 1: Installer les dépendances et configurer le panier Zustand
Installer Zustand :
• npm install zustand
Créer le store panier (/store/cartStore.js) :
• tableau items pour les produits
• propriétés calculées total et count
• méthodes addItem, removeItem, updateQuantity, clearCart
• middleware persist vers localStorage
Points clés :
• persist persiste automatiquement, pas de perte au refresh
• sélecteurs (useCartStore(state => state.addItem)) pour éviter les re-renders inutiles
Cas d'usage : projets moyens (10-50 pages), state management léger - 2
Step 2: Créer l'API Stripe Checkout Session
Installer Stripe :
• npm install stripe @stripe/stripe-js
Variables (.env.local) :
• STRIPE_SECRET_KEY=sk_test_xxxxx (backend, secret)
• NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx (frontend)
• STRIPE_WEBHOOK_SECRET=whsec_xxxxx (signature Webhook)
Route API (/pages/api/create-checkout.js) :
• recevoir les items du panier
• convertir en line_items Stripe (unit_amount × 100)
• créer checkout.sessions (success_url, cancel_url)
• metadata pour données métier (userId, etc.)
• renvoyer sessionId au frontend
Détails :
• Stripe utilise les centimes, prix × 100
• success_url avec placeholder {CHECKOUT_SESSION_ID}
• metadata récupérable dans le Webhook - 3
Step 3: Appeler Stripe Checkout côté frontend
Page checkout (/pages/checkout.js) :
• loadStripe pour Stripe.js
• appeler /api/create-checkout
• stripe.redirectToCheckout() vers la page de paiement
Gestion d'erreurs :
• catch erreurs réseau
• vérifier error de redirectToCheckout
• message utilisateur clair
Page hébergée Stripe :
• pas de formulaire carte à écrire
• validation carte et anti-fraude inclus
• couleurs, logo, polices personnalisables
Attention :
• ne jamais traiter le succès paiement côté frontend
• l'utilisateur peut fermer le navigateur
• création de commande uniquement dans le Webhook - 4
Step 4: Configurer le Webhook pour les commandes
API Webhook (/pages/api/stripe-webhook.js) :
Obligatoire :
• export const config = { api: { bodyParser: false } }
• buffer(req) pour le corps brut
• stripe.webhooks.constructEvent() pour la signature
Événements :
• checkout.session.completed : paiement OK, créer commande
• payment_intent.succeeded : confirmer encaissement
• invoice.payment_failed : échec abonnement
Idempotence :
• vérifier si stripeSessionId existe déjà
• index unique en base
• éviter commandes en double si Webhook rejoué
Métier :
• commande (status: paid)
• déstockage (updateInventory)
• e-mail confirmation (sendOrderConfirmationEmail)
• logs et erreurs
Test local :
• stripe login
• stripe listen --forward-to localhost:3000/api/stripe-webhook
• stripe trigger checkout.session.completed
Critique : bodyParser désactivé, signature vérifiée ; Stripe réessaie 3 jours en cas d'échec - 5
Step 5: Déploiement production et sécurité
Variables :
• clés live (sk_live_xxxxx, pk_live_xxxxx)
• configurer sur Vercel/Netlify
• ne jamais committer la Secret Key
Stripe Dashboard :
• Developers → Webhooks
• endpoint production https://yourdomain.com/api/stripe-webhook
• événements checkout.session.completed, etc.
• Signing secret dans les variables d'environnement
Checklist sécurité :
• logique paiement côté backend uniquement
• signature Webhook validée
• montant paiement = montant commande
• idempotence stripeSessionId
• logs paiement
• alertes si échec Webhook > 5 %
Monitoring :
• taux d'échec Webhook, taux de succès paiement, durée création commande (> 3 s à investiguer)
• Sentry/LogRocket, règles d'alerte, logs Dashboard Stripe
Tests :
• carte test 4242 4242 4242 4242
• flux panier → paiement → Webhook → commande
• scénarios d'échec (stock, Webhook)
FAQ
Redux ou Zustand : lequel pour mon projet ?
• Petit projet (< 10 pages) : Context API suffit, pas de lib dédiée
• Moyen (10-50 pages) : Zustand, léger, courbe d'apprentissage faible, peu de code
• Grand (50+ pages, plusieurs équipes) : Redux Toolkit, outillage et debug solides, communauté mature
Cas concrets :
• Nouveau projet, itération rapide : Zustand
• Déjà sur Redux : garder Redux Toolkit
• Équipe peu familière du state : Zustand plus accessible
Pour un panier : partage inter-composants, persistance, perfs — Zustand convient bien.
Pourquoi ne pas gérer le succès du paiement côté frontend ?
Fiabilité :
• fermeture du navigateur après paiement
• redirection ratée (réseau)
• utilisateur qui ne clique pas sur Terminer
Sécurité :
• code frontend modifiable
• création de commande et stock sensibles, pas côté client
• impossible d'empêcher un faux succès
Bonne pratique :
• logique métier dans le Webhook
• notification serveur Stripe → votre backend (sans le navigateur)
• signature Webhook
• recommandation officielle Stripe : Webhook = source fiable unique pour les commandes
La page success ne sert qu'à l'affichage, pas au métier.
La vérification de signature Webhook échoue toujours — que faire ?
La plus courante (90 %) :
• bodyParser Next.js encore actif
• fix : export const config = { api: { bodyParser: false } }
Autres :
• STRIPE_WEBHOOK_SECRET incorrect dans .env.local
• secret test vs production mélangés
• middleware global qui modifie le body
Debug :
1. confirmer bodyParser: false
2. logger req.headers['stripe-signature']
3. stripe listen --forward-to localhost:3000/api/stripe-webhook
4. lire l'erreur détaillée du CLI
5. utiliser le webhook secret temporaire du CLI
Local :
• Stripe CLI obligatoire pour forward
• secret whsec_xxxxx temporaire
• nouveau secret à chaque redémarrage du CLI → mettre à jour .env.local
Comment éviter plusieurs commandes si le Webhook est appelé en double ?
Base de données :
• index unique sur stripeSessionId
• Prisma : stripeSessionId String @unique
• insertion dupliquée refusée
Code :
• findUnique avant create
• si existe, return sans recréer
Exemple :
```javascript
const existingOrder = await db.order.findUnique({
where: { stripeSessionId: sessionId }
})
if (existingOrder) {
console.log('Commande déjà existante, skip')
return
}
const order = await db.order.create({ ... })
```
Pourquoi :
• Stripe peut renvoyer le Webhook (réseau, retry)
• le code doit tolérer les doublons
• éviter plusieurs commandes et déstockages pour un même paiement
Conseils : logs de chaque appel, monitoring des doublons, alertes.
En production les commandes ne se créent pas — par où commencer ?
1. Webhook reçu ?
• Stripe Dashboard → Developers → Webhooks
• historique et statut succès/échec
• aucun appel → problème d'endpoint
2. Endpoint :
• URL https://yourdomain.com/api/stripe-webhook correcte
• événement checkout.session.completed coché
• endpoint actif
3. Variables :
• STRIPE_WEBHOOK_SECRET correct
• secret de production, pas test
• variables sur Vercel/Netlify
4. Code Webhook :
• bodyParser désactivé
• signature OK
• logs d'erreur
5. Logs applicatifs :
• Vercel Logs, CloudWatch, etc.
• stack trace, handler exécuté ?
6. Test manuel :
• Webhook en échec → Resend dans le Dashboard
• observer succès et message d'erreur
Erreurs fréquentes :
• pas d'endpoint Webhook en prod
• secret test en prod
• firewall bloque Stripe
Validation : carte test, commande, stock, e-mail OK.
10 min de lecture · Publié le: 7 janv. 2026 · Mis à jour le: 27 juil. 2026
Guide complet Next.js
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
Tests E2E Next.js : guide pratique Playwright pour l'automatisation
Retour d'expérience complet du test manuel aux tests E2E automatisés : configuration Playwright, Page Object Model, tests API et intégration CI/CD pour Next.js.
Partie 36 sur 51
Suivant
Guide complet d'upload de fichiers Next.js : upload direct S3/Qiniu via URL présignée
Apprenez à utiliser des URL présignées pour envoyer des fichiers directement vers S3/Qiniu dans Next.js, contourner la limite de 4 Mo, gérer des fichiers jusqu'à 5 Go, avec exemples complets, optimisation et bonnes pratiques de production.
Partie 38 sur 51



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire