Les web services sont des composants essentiels dans le développement web full stack moderne. Ils permettent la communication entre différentes applications, souvent à travers des réseaux, en utilisant des protocoles standardisés. Dans cet article, nous allons explorer les différents types de web services, leur implémentation et leur rôle dans une architecture full stack.
Types de Web Services
1. SOAP (Simple Object Access Protocol)
SOAP est un protocole basé sur XML pour échanger des informations structurées dans des environnements distribués.
Exemple SOAP en Node.js:
const soap = require('soap');
const express = require('express');
const app = express();
const service = {
CalculatorService: {
CalculatorPort: {
add: function(args) {
return { result: args.a + args.b };
}
}
}
};
const xml = require('fs').readFileSync('calculator.wsdl', 'utf8');
app.listen(8000, function() {
soap.listen(app, '/calculator', service, xml);
console.log('SOAP server running at http://localhost:8000/calculator');
});
2. REST (Representational State Transfer)
REST est un style architectural basé sur HTTP, utilisant généralement JSON comme format d’échange.
Exemple REST API avec Express.js:
const express = require('express');
const app = express();
app.use(express.json());
let products = [
{ id: 1, name: 'Laptop', price: 999 },
{ id: 2, name: 'Smartphone', price: 699 }
];
// GET all products
app.get('/api/products', (req, res) => {
res.json(products);
});
// GET single product
app.get('/api/products/:id', (req, res) => {
const product = products.find(p => p.id === parseInt(req.params.id));
if (!product) return res.status(404).send('Product not found');
res.json(product);
});
// POST new product
app.post('/api/products', (req, res) => {
const product = {
id: products.length + 1,
name: req.body.name,
price: req.body.price
};
products.push(product);
res.status(201).json(product);
});
app.listen(3000, () => console.log('REST API running on port 3000'));
3. GraphQL
GraphQL est un langage de requête et un runtime pour les APIs qui permet aux clients de demander exactement les données dont ils ont besoin.
Exemple GraphQL avec Apollo Server:
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
type Book {
title: String
author: String
}
type Query {
books: [Book]
}
`;
const books = [
{ title: 'Clean Code', author: 'Robert C. Martin' },
{ title: 'Design Patterns', author: 'Erich Gamma' }
];
const resolvers = {
Query: {
books: () => books,
},
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`GraphQL server ready at ${url}`);
});
Intégration Full Stack
Dans une architecture full stack, les web services servent de pont entre le frontend et le backend.
Frontend (React) consommant une API REST
import React, { useState, useEffect } from 'react';
function ProductList() {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/products')
.then(response => response.json())
.then(data => {
setProducts(data);
setLoading(false);
});
}, []);
if (loading) return <div>Loading...</div>;
return (
<ul>
{products.map(product => (
<li key={product.id}>
{product.name} - ${product.price}
</li>
))}
</ul>
);
}
export default ProductList;
Backend (Node.js + Express) avec connexion à MongoDB
const express = require('express');
const mongoose = require('mongoose');
const app = express();
// Connexion à MongoDB
mongoose.connect('mongodb://localhost:27017/productsDB', {
useNewUrlParser: true,
useUnifiedTopology: true
});
// Schéma Mongoose
const productSchema = new mongoose.Schema({
name: String,
price: Number
});
const Product = mongoose.model('Product', productSchema);
// Middleware
app.use(express.json());
// Routes
app.get('/api/products', async (req, res) => {
try {
const products = await Product.find();
res.json(products);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
app.post('/api/products', async (req, res) => {
const product = new Product({
name: req.body.name,
price: req.body.price
});
try {
const newProduct = await product.save();
res.status(201).json(newProduct);
} catch (err) {
res.status(400).json({ message: err.message });
}
});
app.listen(3000, () => console.log('Server running on port 3000'));
Sécurité des Web Services
Authentification JWT
Backend (Node.js):
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
// Route d'authentification
app.post('/api/auth', async (req, res) => {
const { username, password } = req.body;
// Vérification des identifiants (simplifié)
if (username !== 'admin' || password !== 'password') {
return res.status(401).json({ message: 'Invalid credentials' });
}
// Création du token
const token = jwt.sign({ username }, 'secret_key', { expiresIn: '1h' });
res.json({ token });
});
// Middleware d'authentification
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, 'secret_key', (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
// Route protégée
app.get('/api/protected', authenticateToken, (req, res) => {
res.json({ message: `Hello ${req.user.username}` });
});
Frontend (React):
const [data, setData] = useState(null);
const login = async () => {
const response = await fetch('/api/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'admin', password: 'password' })
});
const { token } = await response.json();
localStorage.setItem('token', token);
};
const fetchProtectedData = async () => {
const token = localStorage.getItem('token');
const response = await fetch('/api/protected', {
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
setData(data);
};
Bonnes Pratiques
- Validation des données: Toujours valider les entrées côté serveur
- Gestion des erreurs: Retourner des codes HTTP appropriés
- Documentation: Utiliser Swagger/OpenAPI pour documenter vos APIs
- Versioning: Versionner vos APIs (ex: /api/v1/products)
- Limitation de débit: Implémenter rate limiting pour éviter les abus
- Tests: Écrire des tests unitaires et d’intégration
Conclusion
Les web services sont la colonne vertébrale des applications full stack modernes. Que vous choisissiez REST, GraphQL ou SOAP dépend de vos besoins spécifiques. REST reste le choix le plus populaire pour sa simplicité, tandis que GraphQL gagne en popularité pour sa flexibilité. La sécurité et les bonnes pratiques doivent toujours être une priorité lors de la conception et de l’implémentation de vos web services.
En maîtrisant ces technologies et concepts, vous serez en mesure de construire des applications full stack robustes, sécurisées et performantes.