Cours Next.js · Module 8: Déploiement et production
Preview Deployments et environnements de staging
Dans cette leçon6
Dans la Métropole Quantum 2150, aucun système ne va directement sur les serveurs de production. Chaque changement passe par un processus de vérification rigoureux - des preview deployments pour chaque Pull Request, en passant par l'environnement de staging, jusqu'à une promotion contrôlée vers la production. Un tel pipeline garantit que les habitants de Quantum City ne subissent jamais de code défectueux.
Que sont les Preview Deployments ?
Les Preview Deployments sont des déploiements automatiques de l'application générés pour chaque Pull Request. Chaque PR reçoit sa propre URL unique, sur laquelle les changements peuvent être testés avant d'être mergés dans la branche principale.
Vercel - la plateforme créée par les auteurs de Next.js - offre le meilleur support pour les Preview Deployments. Mais d'autres plateformes, comme Netlify ou AWS Amplify, supportent aussi ce concept.
Comment fonctionnent les Preview Deployments sur Vercel ?
- Tu crées une Pull Request sur GitHub
- Vercel détecte automatiquement la nouvelle PR
- Builde l'application avec le code de cette PR
- Génère une URL unique (par ex.
project-git-feature-xyz-team.vercel.app) - Ajoute un commentaire à la PR avec le lien vers la preview
1# Example Preview Deployments URLs:
2# PR #42: my-app-git-feature-login-myteam.vercel.app
3# PR #43: my-app-git-fix-header-myteam.vercel.app
4# PR #44: my-app-git-update-api-myteam.vercel.app
5
6# Each URL is unique and works as long as the PR existsConfiguration de Vercel pour les Preview Deployments
1// vercel.json
2{
3 "git": {
4 "deploymentEnabled": {
5 "main": true,
6 "staging": true
7 }
8 },
9 "github": {
10 "autoAlias": true,
11 "autoJobCancelation": true,
12 "silent": false
13 },
14 "headers": [
15 {
16 "source": "/(.*)",
17 "headers": [
18 {
19 "key": "X-Robots-Tag",
20 "value": "noindex"
21 }
22 ]
23 }
24 ]
25}Note importante : les Preview Deployments devraient avoir le header X-Robots-Tag: noindex pour que les moteurs de recherche n'indexent pas les versions de test !
Environnements de déploiement (dev → staging → prod)
Les projets professionnels utilisent un pipeline de déploiement multi-niveaux :
Hiérarchie des environnements
1Development (dev) → Machine locale du développeur
2 ↓
3Preview → Automatique par PR (Vercel Preview)
4 ↓
5Staging → Miroir de la production (tests finaux)
6 ↓
7Production (prod) → Serveurs de production (utilisateurs finaux)Branch-based deployments
Chaque branche Git peut être mappée à un environnement différent :
1# Branch → environment configuration in Vercel:
2# main → Production (app.quantum-city.com)
3# staging → Staging (staging.quantum-city.com)
4# develop → Development (dev.quantum-city.com)
5# feature/* → Preview (auto-generated URL)
6
7# Workflow du développeur :
8git checkout -b feature/new-dashboard
9# ... codage ...
10git push origin feature/new-dashboard
11# → Automatiquement : Preview Deployment !
12
13# Après review et merge dans staging :
14git checkout staging
15git merge feature/new-dashboard
16git push origin staging
17# → Automatiquement : Staging Deployment !
18
19# Après les tests sur staging, merge dans main :
20git checkout main
21git merge staging
22git push origin main
23# → Automatiquement : Production Deployment !Variables d'environnement par environnement
Chaque environnement a ses propres variables :
1// Vercel CLI - adding variables per environment
2// vercel env add DATABASE_URL development
3// vercel env add DATABASE_URL preview
4// vercel env add DATABASE_URL production
5
6// Dans le code, on vérifie l'environnement :
7// app/api/config/route.ts
8import { NextResponse } from 'next/server';
9
10export async function GET() {
11 const environment = process.env.VERCEL_ENV; // 'production' | 'preview' | 'development'
12 const gitBranch = process.env.VERCEL_GIT_COMMIT_REF; // par ex. 'feature/login'
13 const commitSha = process.env.VERCEL_GIT_COMMIT_SHA; // hash du commit
14
15 return NextResponse.json({
16 environment,
17 branch: gitBranch,
18 commit: commitSha?.slice(0, 7),
19 // Different databases per environment
20 database: environment === 'production'
21 ? 'prod-cluster'
22 : environment === 'preview'
23 ? 'preview-cluster'
24 : 'dev-cluster',
25 });
26}Protected Deployments
Dans les environnements de staging et de preview, il vaut la peine de protéger l'accès pour que des personnes non autorisées ne voient pas les changements non publiés :
1// middleware.ts - protection de preview et staging
2import { NextRequest, NextResponse } from 'next/server';
3
4export function middleware(request: NextRequest) {
5 const environment = process.env.VERCEL_ENV;
6
7 // Ne protège pas la production
8 if (environment === 'production') {
9 return NextResponse.next();
10 }
11
12 // Check authorization for preview/staging
13 const authHeader = request.headers.get('authorization');
14 const previewToken = process.env.PREVIEW_ACCESS_TOKEN;
15
16 // Allow access with a token or cookie
17 const hasToken = authHeader === 'Bearer ' + previewToken;
18 const hasCookie = request.cookies.get('preview-access')?.value === previewToken;
19
20 if (!hasToken && !hasCookie) {
21 // Redirige vers la page de connexion preview
22 return NextResponse.redirect(new URL('/preview-login', request.url));
23 }
24
25 return NextResponse.next();
26}
27
28export const config = {
29 matcher: ['/((?!preview-login|api|_next/static|favicon.ico).*)'],
30};Deployment Hooks - automatisation du déploiement
Les Deploy Hooks sont des URL de webhook qui déclenchent un nouveau déploiement quand elles sont appelées :
1# Création d'un Deploy Hook dans Vercel :
2# Settings → Git → Deploy Hooks
3# Nom : "CMS Content Update"
4# Branch: main
5# → Génère l'URL : https://api.vercel.com/v1/integrations/deploy/prj_xxx/yyy
6
7# Usage with CMS (e.g., after publishing an article):
8curl -X POST https://api.vercel.com/v1/integrations/deploy/prj_xxx/yyy
9
10# Usage in GitHub Actions:
11# - name: Trigger Vercel Deploy
12# run: curl -X POST ${{ secrets.VERCEL_DEPLOY_HOOK }}
13
14# Usage with Strapi/Sanity webhook:
15# After publishing content → POST to Deploy Hook URL
16# → Vercel rebuilds the page with new data (ISR/SSG)Workflow pratique avec les Deploy Hooks
1// app/api/webhook/cms/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4export async function POST(request: NextRequest) {
5 // Vérification du webhook du CMS
6 const signature = request.headers.get('x-webhook-signature');
7 const secret = process.env.CMS_WEBHOOK_SECRET;
8
9 if (!verifySignature(signature, secret)) {
10 return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
11 }
12
13 // Trigger revalidation or new deploy
14 const deployHookUrl = process.env.VERCEL_DEPLOY_HOOK;
15
16 if (deployHookUrl) {
17 await fetch(deployHookUrl, { method: 'POST' });
18 return NextResponse.json({ status: 'Deploy triggered' });
19 }
20
21 return NextResponse.json({ status: 'No deploy hook configured' });
22}
23
24function verifySignature(signature: string | null, secret: string | undefined): boolean {
25 if (!signature || !secret) return false;
26 // Implémentation de la vérification HMAC
27 return true;
28}Environment Promotion (dev → staging → prod)
La promotion d'environnement est le processus contrôlé de déplacement du code entre les environnements :
1# Stratégie 1 : Git-flow avec auto-deploy
2# develop → auto-deploy to dev
3# staging → auto-deploy to staging
4# main → auto-deploy to production
5
6# Merge workflow:
7git checkout staging
8git merge develop # Promote dev → staging
9git push origin staging # Auto-deploy sur staging
10
11# Après les tests sur staging :
12git checkout main
13git merge staging # Promote staging → prod
14git push origin main # Auto-deploy sur production
15
16# Stratégie 2 : Vercel Promote (sans merge)
17# Vercel permet de promouvoir un déploiement existant :
18vercel promote [deployment-url] --scope=team
19
20# Cela déplace EXACTEMENT le même build en production
21# Sans rebuild - garantie d'identité !Résumé
Les Preview Deployments et les environnements de staging sont les fondations d'un processus de déploiement professionnel :
- Preview Deployments - déploiements automatiques par PR, idéaux pour la revue de code
- Branch-based deployments - mapping des branches vers les environnements (dev/staging/prod)
- Environment promotion - flux contrôlé du code entre les environnements
- Protected Deployments - protection des environnements de preview et staging contre les accès non autorisés
- Deploy Hooks - déclenchement automatique des déploiements depuis un CMS ou une CI/CD
Dans la Métropole Quantum 2150, aucun système ne part en production sans le cycle complet preview → staging → production. Cela garantit la stabilité et la qualité des services pour tous les habitants de la ville cyberpunk.
Code de cette leçon : App.tsx
1import React, { useState } from 'react';
2
3// Demo : Preview Deployments et environnements de staging
4interface Deployment {
5 id: string;
6 branch: string;
7 env: 'production' | 'staging' | 'preview';
8 url: string;
9 status: 'ready' | 'building' | 'error';
10 pr?: number;
11 commit: string;
12 age: string;
13}
14
15const deployments: Deployment[] = [
16 { id: '1', branch: 'main', env: 'production', url: 'app.quantum-city.com', status: 'ready', commit: 'a1b2c3d', age: '2h ago' },
17 { id: '2', branch: 'staging', env: 'staging', url: 'staging.quantum-city.com', status: 'ready', commit: 'e4f5g6h', age: '1h ago' },
18 { id: '3', branch: 'feature/new-dashboard', env: 'preview', url: 'app-git-feature-new-dashboard-team.vercel.app', status: 'ready', pr: 42, commit: 'i7j8k9l', age: '30m ago' },
19 { id: '4', branch: 'fix/header-layout', env: 'preview', url: 'app-git-fix-header-layout-team.vercel.app', status: 'building', pr: 43, commit: 'm0n1o2p', age: '5m ago' },
20 { id: '5', branch: 'feature/auth-update', env: 'preview', url: 'app-git-feature-auth-team.vercel.app', status: 'error', pr: 44, commit: 'q3r4s5t', age: '15m ago' },
21];
22
23const envColors: Record<string, string> = { production: '#4caf50', staging: '#ff9800', preview: '#7c3aed' };
24const statusColors: Record<string, string> = { ready: '#4caf50', building: '#2196f3', error: '#f44336' };
25
26const pipeline = [
27 { name: 'Development', desc: 'Machine locale', icon: '1' },
28 { name: 'Preview', desc: 'Auto par PR', icon: '2' },
29 { name: 'Staging', desc: 'Tests finaux', icon: '3' },
30 { name: 'Production', desc: 'Utilisateurs', icon: '4' },
31];
32
33export default function PreviewDeploymentsDemo() {
34 const [filter, setFilter] = useState<'all' | 'production' | 'staging' | 'preview'>('all');
35 const [showPipeline, setShowPipeline] = useState(true);
36
37 const filtered = filter === 'all' ? deployments : deployments.filter(d => d.env === filter);
38
39 return (
40 <div style={{ background: '#0f0f23', minHeight: '100vh', padding: '20px', color: '#e0e0e0', fontFamily: 'system-ui' }}>
41 <h1 style={{ color: '#64ffda', marginBottom: '8px' }}>Preview Deployments</h1>
42 <p style={{ color: '#8892b0', marginBottom: '24px' }}>Pipeline: dev -> preview -> staging -> production</p>
43
44 {showPipeline && (
45 <div style={{ display: 'flex', gap: '4px', marginBottom: '24px', alignItems: 'center' }}>
46 {pipeline.map((step, i) => (
47 <React.Fragment key={step.name}>
48 <div style={{
49 flex: 1, padding: '12px', borderRadius: '8px', textAlign: 'center',
50 background: '#1a1a2e', border: '1px solid #333',
51 }}>
52 <div style={{ color: '#64ffda', fontWeight: 'bold', fontSize: '20px' }}>{step.icon}</div>
53 <div style={{ color: '#fff', fontSize: '13px', fontWeight: 'bold' }}>{step.name}</div>
54 <div style={{ color: '#8892b0', fontSize: '11px' }}>{step.desc}</div>
55 </div>
56 {i < pipeline.length - 1 && <span style={{ color: '#64ffda', fontSize: '20px' }}>→</span>}
57 </React.Fragment>
58 ))}
59 </div>
60 )}
61
62 <div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
63 {(['all', 'production', 'staging', 'preview'] as const).map(f => (
64 <button key={f} onClick={() => setFilter(f)} style={{
65 padding: '6px 12px', borderRadius: '6px', border: 'none', cursor: 'pointer', fontSize: '13px',
66 background: filter === f ? (f === 'all' ? '#64ffda' : envColors[f]) : '#1a1a2e',
67 color: filter === f ? '#0f0f23' : '#8892b0', fontWeight: 'bold',
68 }}>{f === 'all' ? 'Tous' : f.charAt(0).toUpperCase() + f.slice(1)}</button>
69 ))}
70 <button onClick={() => setShowPipeline(!showPipeline)} style={{
71 padding: '6px 12px', borderRadius: '6px', border: '1px solid #333', background: 'transparent', color: '#8892b0', cursor: 'pointer', fontSize: '13px', marginLeft: 'auto',
72 }}>{showPipeline ? 'Masquer le pipeline' : 'Afficher le pipeline'}</button>
73 </div>
74
75 <div style={{ display: 'grid', gap: '8px' }}>
76 {filtered.map(d => (
77 <div key={d.id} style={{
78 padding: '14px', borderRadius: '8px', background: '#1a1a2e',
79 border: '1px solid ' + envColors[d.env] + '40',
80 }}>
81 <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '6px' }}>
82 <span style={{ width: '8px', height: '8px', borderRadius: '50%', background: statusColors[d.status], flexShrink: 0 }} />
83 <code style={{ color: '#fff', fontWeight: 'bold', fontSize: '13px' }}>{d.branch}</code>
84 <span style={{ fontSize: '10px', padding: '2px 8px', borderRadius: '4px', background: envColors[d.env] + '20', color: envColors[d.env] }}>{d.env}</span>
85 {d.pr && <span style={{ fontSize: '11px', color: '#8892b0' }}>PR #{d.pr}</span>}
86 <span style={{ fontSize: '11px', color: '#666', marginLeft: 'auto' }}>{d.age}</span>
87 </div>
88 <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
89 <code style={{ color: '#8892b0', fontSize: '12px' }}>{d.url}</code>
90 <code style={{ color: '#666', fontSize: '11px', marginLeft: 'auto' }}>{d.commit}</code>
91 </div>
92 </div>
93 ))}
94 </div>
95 </div>
96 );
97}Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Les Preview Deployments sur Vercel sont générés automatiquement :
2. Un CDN (Content Delivery Network) fournit :
Voici 2 des 11 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Tri vertical
Place les environnements dans le bon ordre du pipeline de déploiement :
- Tri vertical
Place les éléments dans le bon ordre : définir l'expérience → répartir le trafic → suivre les métriques
- Tri vertical
Place les étapes d'analyse de la taille du bundle dans le bon ordre :
- Tri vertical
Place les éléments dans le bon ordre : auditer les dépendances → définir les en-têtes CSP → activer le HTTPS
- Tri vertical
Place les éléments dans le bon ordre : commit du code vers le dépôt → déclenchement du pipeline CI/CD
- Tri horizontal
Place la configuration ESLint dans .eslintrc.json
- Tri vertical
Place les étapes de la gestion d'un incident en production dans le bon ordre
- Clique dans l'ordre
Place la commande de build Docker :
- Éditeur de code
Écris en Markdown une checklist de déploiement en production avec cinq sections (titres) : sécurité, performances, monitoring, sauvegardes et conformité. Ajoute au moins deux cases à cocher (- [ ] ...) dans chaque section.
- Clique dans l'ordre
Place la configuration de déploiement Vercel :