De zéro à une app fullstack avec Next.js et l’IA : le projet parfait pour ton portfolio
Contenu
Objectifs
Initialisé un projet Next.js App Router avec TypeScript et Tailwind.
Créé une route API serverless.
Consommé l’API OpenAI (modèle GPT-4o-mini) en back-end.
Géré l’état et les appels asynchrones en React.
Déployé une application fullstack avec variables d’environnement sécurisées.
Prérequis
Node.js 18+ installé (téléchargement)
Visual Studio Code
Un compte GitHub (pour le déploiement)
Un compte OpenAI avec une clé API – platform.openai.com
Tu obtiens des crédits gratuits à l’inscription, largement suffisants pour ce projet.
Un compte Vercel (gratuit, connexion via GitHub).
Programme détaillé
Étape 1 – Créer le projet Next.js
Ouvre un terminal et tape :
npx create-next-app@latest mon-generateur-portfolio
Pendant l’installation, réponds ainsi aux questions :
✔ Would you like to use TypeScript? … No / Yes → Yes ✔ Would you like to use ESLint? … Yes ✔ Would you like to use Tailwind CSS? … Yes ✔ Would you like to use `src/` directory? … Yes ✔ Would you like to use App Router? … Yes ✔ Would you like to customize the default import alias? … No
Quand c’est terminé, rends-toi dans le dossier :
cd mon-generateur-portfolio
Ouvre le projet dans VS Code :
code .
Lance le serveur de développement pour vérifier :
npm run dev
Rendez-vous sur http://localhost:3000 : la page par défaut de Next.js apparaît.
Étape 2 – Mise en place de l’interface
On va construire une page simple avec un champ texte et un bouton.
Remplace le contenu de src/app/page.tsx par :
'use client'; import { useState } from 'react'; export default function Home() { const [title, setTitle] = useState(''); const [result, setResult] = useState(''); const [loading, setLoading] = useState(false); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); // Appel à l'API à venir à l'étape 3 setLoading(false); }; return ( <main className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-900 to-indigo-950 p-4"> <div className="bg-white/10 backdrop-blur-md rounded-xl p-8 w-full max-w-2xl shadow-2xl border border-white/20"> <h1 className="text-3xl font-bold text-white mb-2">Générateur de description de projet</h1> <p className="text-gray-300 mb-6"> Donne le titre de ton projet, l'IA rédige une description prête pour ton portfolio. </p> <form onSubmit={handleSubmit} className="flex flex-col gap-4"> <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Ex : Application de chat en temps réel avec Socket.io" className="p-3 rounded-md bg-gray-800 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-indigo-500" required /> <button type="submit" disabled={loading} className="bg-indigo-600 hover:bg-indigo-700 disabled:bg-gray-600 text-white font-semibold py-3 rounded-md transition" > {loading ? 'Génération en cours...' : 'Générer la description'} </button> </form> {result && ( <div className="mt-6 p-4 bg-gray-800 rounded-md border border-gray-700"> <h2 className="text-sm text-indigo-400 mb-1">Résultat :</h2> <p className="text-white whitespace-pre-wrap">{result}</p> </div> )} </div> </main> ); }
Quelques explications :
-
On utilise le hook
useStatepour gérer le titre et le résultat. -
Le composant est en
'use client'car il y a des interactions (formulaire). -
Le style est entièrement en Tailwind, avec un fond dégradé et un conteneur vitré.
Tu peux déjà tester l’affichage. Le bouton ne fait rien pour l’instant, on s’en occupe maintenant.
Étape 3 – Créer la route API Next.js qui appelle OpenAI
L’intelligence artificielle sera appelée côté serveur pour ne pas exposer ta clé API. Next.js permet de créer des routes API très simplement.
-
Crée un fichier
.env.localà la racine du projet :
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Remplace par ta clé réelle (ne la partage jamais, ni sur GitHub).
-
Crée le dossier
src/app/api/generate/route.ts. Oui, il faut créer les sous-dossiers. -
À l’intérieur, écris :
import { NextRequest, NextResponse } from 'next/server'; export async function POST(request: NextRequest) { const { title } = await request.json(); if (!title) { return NextResponse.json({ error: 'Titre requis' }, { status: 400 }); } const prompt = `Tu es un assistant qui aide les étudiants à rédiger des descriptions de projets tech pour leur portfolio. Écris une description professionnelle et attrayante pour un projet intitulé "${title}". Structure la réponse en deux parties : 1. Une phrase d'accroche. 2. Un court paragraphe (3-4 lignes) détaillant les fonctionnalités, la stack technique et la valeur ajoutée. Sois enthousiaste mais crédible.`; try { 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-4o-mini', // le modèle le moins cher et très rapide messages: [{ role: 'user', content: prompt }], temperature: 0.7, }), }); const data = await response.json(); if (data.error) { return NextResponse.json({ error: data.error.message }, { status: 500 }); } const generatedText = data.choices[0].message.content; return NextResponse.json({ result: generatedText }); } catch (error) { return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }); } }
Explication rapide :
-
On reçoit le titre via POST.
-
On construit un
promptclair. -
On envoie à l’API OpenAI.
-
On renvoie le texte généré.
Redémarre le serveur (Ctrl+C puis npm run dev) pour prendre en compte le fichier .env.local.
Étape 4 – Relier le front-end à l’API
Retourne dans src/app/page.tsx et complète la fonction handleSubmit :
const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); setResult(''); try { const res = await fetch('/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title }), }); const data = await res.json(); if (data.error) { setResult(`Erreur : ${data.error}`); } else { setResult(data.result); } } catch (err) { setResult('Impossible de générer la description.'); } finally { setLoading(false); } };
Teste l’application : écris un titre comme “To-Do List collaborative en React et Firebase” et clique sur le bouton. Après quelques secondes, la description apparaît.
🎉 Félicitations, tu as intégré l’IA dans ton app !
Étape 5 – Déployer gratuitement sur Vercel
-
Initialise un dépôt Git :
git init git add . git commit -m "Initial commit"
-
Pousse vers GitHub (crée un repo vide sur GitHub auparavant) :
git remote add origin https://github.com/ton-utilisateur/mon-generateur-portfolio.git git branch -M main git push -u origin main
-
Va sur vercel.com, connecte-toi avec GitHub, et importe ce dépôt.
-
Dans les paramètres du projet sur Vercel, ajoute la variable d’environnement
OPENAI_API_KEYavec ta clé. -
Lance le déploiement. En 30 secondes, ton app est en ligne avec une URL publique !
Étape 6 – Mettre en valeur le projet sur ton portfolio
Pour que ce projet brille, fais ceci :
-
Rédige un README : description du projet, technologies, capture d’écran, lien vers la démo.
-
Ajoute une page “projet” dédiée sur ton site perso, avec un lien direct.
-
Poste sur LinkedIn : “J’ai créé une app qui génère des descriptions de projets avec l’IA – tech : Next.js, Tailwind, OpenAI API”. Les recruteurs adorent ce type d’initiative.
-
Améliore l’application (optionnel) :
-
Ajoute un bouton pour copier la description.
-
Gère deux langues (français / anglais).
-
Permet de générer plusieurs variantes.
-
Programme
Étape 1 – Créer le projet Next.js
Ouvre un terminal et tape :
npx create-next-app@latest mon-generateur-portfolio
Pendant l’installation, réponds ainsi aux questions :
✔ Would you like to use TypeScript? … No / Yes → Yes ✔ Would you like to use ESLint? … Yes ✔ Would you like to use Tailwind CSS? … Yes ✔ Would you like to use `src/` directory? … Yes ✔ Would you like to use App Router? … Yes ✔ Would you like to customize the default import alias? … No
Quand c’est terminé, rends-toi dans le dossier :
cd mon-generateur-portfolio
Ouvre le projet dans VS Code :
code .
Lance le serveur de développement pour vérifier :
npm run dev
Rendez-vous sur http://localhost:3000 : la page par défaut de Next.js apparaît.
Étape 2 – Mise en place de l’interface
On va construire une page simple avec un champ texte et un bouton.
Remplace le contenu de src/app/page.tsx par :
'use client'; import { useState } from 'react'; export default function Home() { const [title, setTitle] = useState(''); const [result, setResult] = useState(''); const [loading, setLoading] = useState(false); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); // Appel à l'API à venir à l'étape 3 setLoading(false); }; return ( <main className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-900 to-indigo-950 p-4"> <div className="bg-white/10 backdrop-blur-md rounded-xl p-8 w-full max-w-2xl shadow-2xl border border-white/20"> <h1 className="text-3xl font-bold text-white mb-2">Générateur de description de projet</h1> <p className="text-gray-300 mb-6"> Donne le titre de ton projet, l'IA rédige une description prête pour ton portfolio. </p> <form onSubmit={handleSubmit} className="flex flex-col gap-4"> <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Ex : Application de chat en temps réel avec Socket.io" className="p-3 rounded-md bg-gray-800 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-indigo-500" required /> <button type="submit" disabled={loading} className="bg-indigo-600 hover:bg-indigo-700 disabled:bg-gray-600 text-white font-semibold py-3 rounded-md transition" > {loading ? 'Génération en cours...' : 'Générer la description'} </button> </form> {result && ( <div className="mt-6 p-4 bg-gray-800 rounded-md border border-gray-700"> <h2 className="text-sm text-indigo-400 mb-1">Résultat :</h2> <p className="text-white whitespace-pre-wrap">{result}</p> </div> )} </div> </main> ); }
Quelques explications :
-
On utilise le hook
useStatepour gérer le titre et le résultat. -
Le composant est en
'use client'car il y a des interactions (formulaire). -
Le style est entièrement en Tailwind, avec un fond dégradé et un conteneur vitré.
Tu peux déjà tester l’affichage. Le bouton ne fait rien pour l’instant, on s’en occupe maintenant.
Étape 3 – Créer la route API Next.js qui appelle OpenAI
L’intelligence artificielle sera appelée côté serveur pour ne pas exposer ta clé API. Next.js permet de créer des routes API très simplement.
-
Crée un fichier
.env.localà la racine du projet :
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Remplace par ta clé réelle (ne la partage jamais, ni sur GitHub).
-
Crée le dossier
src/app/api/generate/route.ts. Oui, il faut créer les sous-dossiers. -
À l’intérieur, écris :
import { NextRequest, NextResponse } from 'next/server'; export async function POST(request: NextRequest) { const { title } = await request.json(); if (!title) { return NextResponse.json({ error: 'Titre requis' }, { status: 400 }); } const prompt = `Tu es un assistant qui aide les étudiants à rédiger des descriptions de projets tech pour leur portfolio. Écris une description professionnelle et attrayante pour un projet intitulé "${title}". Structure la réponse en deux parties : 1. Une phrase d'accroche. 2. Un court paragraphe (3-4 lignes) détaillant les fonctionnalités, la stack technique et la valeur ajoutée. Sois enthousiaste mais crédible.`; try { 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-4o-mini', // le modèle le moins cher et très rapide messages: [{ role: 'user', content: prompt }], temperature: 0.7, }), }); const data = await response.json(); if (data.error) { return NextResponse.json({ error: data.error.message }, { status: 500 }); } const generatedText = data.choices[0].message.content; return NextResponse.json({ result: generatedText }); } catch (error) { return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }); } }
Explication rapide :
-
On reçoit le titre via POST.
-
On construit un
promptclair. -
On envoie à l’API OpenAI.
-
On renvoie le texte généré.
Redémarre le serveur (Ctrl+C puis npm run dev) pour prendre en compte le fichier .env.local.
Étape 4 – Relier le front-end à l’API
Retourne dans src/app/page.tsx et complète la fonction handleSubmit :
const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); setResult(''); try { const res = await fetch('/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title }), }); const data = await res.json(); if (data.error) { setResult(`Erreur : ${data.error}`); } else { setResult(data.result); } } catch (err) { setResult('Impossible de générer la description.'); } finally { setLoading(false); } };
Teste l’application : écris un titre comme “To-Do List collaborative en React et Firebase” et clique sur le bouton. Après quelques secondes, la description apparaît.
🎉 Félicitations, tu as intégré l’IA dans ton app !
Étape 5 – Déployer gratuitement sur Vercel
-
Initialise un dépôt Git :
git init git add . git commit -m "Initial commit"
-
Pousse vers GitHub (crée un repo vide sur GitHub auparavant) :
git remote add origin https://github.com/ton-utilisateur/mon-generateur-portfolio.git git branch -M main git push -u origin main
-
Va sur vercel.com, connecte-toi avec GitHub, et importe ce dépôt.
-
Dans les paramètres du projet sur Vercel, ajoute la variable d’environnement
OPENAI_API_KEYavec ta clé. -
Lance le déploiement. En 30 secondes, ton app est en ligne avec une URL publique !
Étape 6 – Mettre en valeur le projet sur ton portfolio
Pour que ce projet brille, fais ceci :
-
Rédige un README : description du projet, technologies, capture d’écran, lien vers la démo.
-
Ajoute une page “projet” dédiée sur ton site perso, avec un lien direct.
-
Poste sur LinkedIn : “J’ai créé une app qui génère des descriptions de projets avec l’IA – tech : Next.js, Tailwind, OpenAI API”. Les recruteurs adorent ce type d’initiative.
-
Améliore l’application (optionnel) :
-
Ajoute un bouton pour copier la description.
-
Gère deux langues (français / anglais).
-
Permet de générer plusieurs variantes.
-
Formateur / Entreprise
Contenu à venir...