Intégration Web
Création d'une application web avec ChatGPT
10-15 min
Intégration de ChatGPT dans une application web
Architecture de base
Pour intégrer ChatGPT dans une application web, nous allons utiliser une architecture client-serveur :
Client (Frontend) <-> Serveur (Backend) <-> API ChatGPT
Structure du projet
chatgpt-app/
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ ├── services/
│ │ └── styles/
│ ├── package.json
│ └── index.html
├── backend/
│ ├── src/
│ │ ├── controllers/
│ │ ├── services/
│ │ └── config/
│ ├── package.json
│ └── .env
└── README.md
Backend (Node.js/Express)
Configuration du serveur
// backend/src/config/config.js
require('dotenv').config();
module.exports = {
port: process.env.PORT || 3000,
openaiApiKey: process.env.OPENAI_API_KEY,
corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:5173'
};
Service ChatGPT
// backend/src/services/chatgpt.service.js
const { Configuration, OpenAIApi } = require('openai');
const config = require('../config/config');
const configuration = new Configuration({
apiKey: config.openaiApiKey,
});
const openai = new OpenAIApi(configuration);
class ChatGPTService {
async generateResponse(prompt, context = []) {
try {
const completion = await openai.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [
{ role: "system", content: "Vous êtes un assistant utile et amical." },
...context,
{ role: "user", content: prompt }
],
temperature: 0.7,
max_tokens: 500
});
return completion.data.choices[0].message.content;
} catch (error) {
console.error('Erreur ChatGPT:', error);
throw new Error('Erreur lors de la génération de la réponse');
}
}
}
module.exports = new ChatGPTService();
Contrôleur API
// backend/src/controllers/chat.controller.js
const ChatGPTService = require('../services/chatgpt.service');
class ChatController {
async handleChat(req, res) {
try {
const { message, context } = req.body;
if (!message) {
return res.status(400).json({ error: 'Le message est requis' });
}
const response = await ChatGPTService.generateResponse(message, context);
res.json({ response });
} catch (error) {
console.error('Erreur contrôleur:', error);
res.status(500).json({ error: 'Erreur serveur' });
}
}
}
module.exports = new ChatController();
Routes API
// backend/src/routes/chat.routes.js
const express = require('express');
const router = express.Router();
const ChatController = require('../controllers/chat.controller');
router.post('/chat', ChatController.handleChat);
module.exports = router;
Serveur Express
// backend/src/server.js
const express = require('express');
const cors = require('cors');
const config = require('./config/config');
const chatRoutes = require('./routes/chat.routes');
const app = express();
app.use(cors({
origin: config.corsOrigin
}));
app.use(express.json());
app.use('/api', chatRoutes);
app.listen(config.port, () => {
console.log(`Serveur démarré sur le port ${config.port}`);
});
Frontend (React)
Service API
// frontend/src/services/api.service.js
const API_URL = 'http://localhost:3000/api';
export const chatService = {
async sendMessage(message, context = []) {
try {
const response = await fetch(`${API_URL}/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message, context }),
});
if (!response.ok) {
throw new Error('Erreur réseau');
}
return await response.json();
} catch (error) {
console.error('Erreur API:', error);
throw error;
}
}
};
Composant Chat
// frontend/src/components/Chat.jsx
import React, { useState, useRef, useEffect } from 'react';
import { chatService } from '../services/api.service';
import '../styles/Chat.css';
const Chat = () => {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
const handleSubmit = async (e) => {
e.preventDefault();
if (!input.trim()) return;
const userMessage = input.trim();
setInput('');
setLoading(true);
// Ajouter le message de l'utilisateur
setMessages(prev => [...prev, { role: 'user', content: userMessage }]);
try {
// Préparer le contexte
const context = messages.map(msg => ({
role: msg.role,
content: msg.content
}));
// Envoyer la requête
const { response } = await chatService.sendMessage(userMessage, context);
// Ajouter la réponse
setMessages(prev => [...prev, { role: 'assistant', content: response }]);
} catch (error) {
setMessages(prev => [...prev, {
role: 'system',
content: 'Désolé, une erreur est survenue. Veuillez réessayer.'
}]);
} finally {
setLoading(false);
}
};
return (
<div className="chat-container">
<div className="messages">
{messages.map((message, index) => (
<div key={index} className={`message ${message.role}`}>
{message.content}
</div>
))}
{loading && (
<div className="message assistant loading">
<div className="typing-indicator">
<span></span>
<span></span>
<span></span>
</div>
</div>
)}
<div ref={messagesEndRef} />
</div>
<form onSubmit={handleSubmit} className="input-form">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Tapez votre message..."
disabled={loading}
/>
<button type="submit" disabled={loading}>
Envoyer
</button>
</form>
</div>
);
};
export default Chat;
Styles CSS
/* frontend/src/styles/Chat.css */
.chat-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
height: 100vh;
display: flex;
flex-direction: column;
}
.messages {
flex-grow: 1;
overflow-y: auto;
padding: 20px;
background: #f5f5f5;
border-radius: 10px;
margin-bottom: 20px;
}
.message {
margin-bottom: 15px;
padding: 10px 15px;
border-radius: 15px;
max-width: 70%;
}
.message.user {
background: #007bff;
color: white;
margin-left: auto;
}
.message.assistant {
background: white;
color: #333;
margin-right: auto;
}
.message.system {
background: #ff4444;
color: white;
margin: 10px auto;
text-align: center;
}
.input-form {
display: flex;
gap: 10px;
}
input {
flex-grow: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 16px;
}
button {
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
button:disabled {
background: #cccccc;
cursor: not-allowed;
}
.typing-indicator {
display: flex;
gap: 5px;
}
.typing-indicator span {
width: 8px;
height: 8px;
background: #666;
border-radius: 50%;
animation: typing 1s infinite ease-in-out;
}
.typing-indicator span:nth-child(2) {
animation-delay: 0.2s;
}
.typing-indicator span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes typing {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
Sécurité et bonnes pratiques
1. Protection des clés API
// backend/.env
OPENAI_API_KEY=votre_clé_api
PORT=3000
CORS_ORIGIN=http://localhost:5173
2. Rate Limiting
// backend/src/middleware/rateLimiter.js
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limite chaque IP à 100 requêtes par fenêtre
});
module.exports = limiter;
3. Validation des entrées
// backend/src/middleware/validator.js
const { body, validationResult } = require('express-validator');
const chatValidation = [
body('message')
.trim()
.notEmpty()
.withMessage('Le message est requis')
.isLength({ max: 1000 })
.withMessage('Le message ne doit pas dépasser 1000 caractères'),
body('context')
.optional()
.isArray()
.withMessage('Le contexte doit être un tableau')
];
const validate = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
};
module.exports = {
chatValidation,
validate
};
Déploiement
Configuration de production
// backend/src/config/production.js
module.exports = {
port: process.env.PORT || 3000,
openaiApiKey: process.env.OPENAI_API_KEY,
corsOrigin: process.env.CORS_ORIGIN,
rateLimit: {
windowMs: 15 * 60 * 1000,
max: 100
}
};
Script de déploiement
#!/bin/bash
# deploy.sh
# Build frontend
cd frontend
npm run build
# Deploy backend
cd ../backend
npm install --production
pm2 start src/server.js --name "chatgpt-app"
Tests
Tests unitaires (Jest)
// backend/src/tests/chat.service.test.js
const ChatGPTService = require('../services/chatgpt.service');
describe('ChatGPTService', () => {
test('generateResponse retourne une réponse valide', async () => {
const prompt = 'Bonjour';
const response = await ChatGPTService.generateResponse(prompt);
expect(response).toBeDefined();
expect(typeof response).toBe('string');
expect(response.length).toBeGreaterThan(0);
});
});
Tests d’intégration
// backend/src/tests/chat.integration.test.js
const request = require('supertest');
const app = require('../server');
describe('Chat API', () => {
test('POST /api/chat retourne une réponse valide', async () => {
const response = await request(app)
.post('/api/chat')
.send({ message: 'Bonjour' });
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('response');
});
});
Commentaires
Les commentaires sont alimentés par GitHub Discussions
Connectez-vous avec GitHub pour participer à la discussion