WebSockets avec NestJS : un tutoriel approfondi en français
Pourquoi WebSockets avec NestJS ?
Contexte réel : pourquoi un dev a besoin de ça au quotidien
Les WebSockets sont essentiels pour les applications nécessitant une communication bidirectionnelle et en temps réel entre le client et le serveur. Ils permettent des interactions plus fluides, comme la mise à jour instantanée de l'interface utilisateur ou la collaboration en temps réel.
Un cas d'utilisation concret en 2-3 phrases
Imaginez une application de chat : chaque fois qu'un message est envoyé, tous les autres utilisateurs doivent recevoir immédiatement cette nouvelle information. Les WebSockets sont idéaux pour ce type d'application, car ils permettent aux utilisateurs de recevoir des mises à jour instantanées sans avoir besoin de recharger la page.
Prerequis
Connaissances nécessaires :
- Connaissance de base de JavaScript et Node.js
- Expérience avec NestJS (10+ ans d'expérience recommandée)
- Compréhension des concepts de base d'Angular ou React pour une application front-end (facultatif mais utile)
Outils à installer :
- Node.js v14.x ou plus récent
- npm ou yarn pour la gestion des dépendances
- Un éditeur de code comme Visual Studio Code
Concepts fondamentaux
1. WebSockets : Connexion persistante entre client et serveur
Les WebSockets permettent une connexion persistante entre le client et le serveur, ce qui signifie qu'ils restent ouverts pour la communication en temps réel. C'est le contraire des requêtes HTTP traditionnelles, qui sont fermées après l'envoi de la réponse.
Schéma mental :
Client (Browser) <--> WebSocket Server
2. Socket.IO : Bibliothèque facilitant la communication WebSockets
Socket.IO est une bibliothèque JavaScript côté client et serveur qui simplifie le processus de connexion WebSockets et gère les émissions et les reçus d'événements.
Schéma mental :
Client (Browser) <--> Socket.IO Client <--> WebSocket Server <--> Socket.IO Server
3. Pipes dans NestJS : Validation des données
Les pipes sont utilisés pour transformer et valider les données entrantes avant qu'elles ne soient traitées par le contrôleur ou la fonction service.
Schéma mental :
Client (Browser) <--> WebSocket Server <--> Pipe <--> Controller
Mise en pratique : projet fil rouge
1. Création d'un nouveau projet NestJS
nest new websocket-chat-app
cd websocket-chat-app
2. Installation des dépendances nécessaires
npm install @nestjs/websockets @nestjs/platform-socket.io socket.io-adapter
npm install --save-dev @types/socket.io
3. Création d'un module pour les WebSockets
// src/chat/chat.module.ts
import { Module } from '@nestjs/common';
import { ChatGateway } from './chat.gateway';
@Module({
providers: [ChatGateway],
})
export class ChatModule {}
4. Création du Gateway WebSocket
// src/chat/chat.gateway.ts
import {
WebSocketGateway,
SubscribeMessage,
MessageBody,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
@WebSocketGateway()
export class ChatGateway {
@SubscribeMessage('message')
handleMessage(client: Socket, payload: any): void {
client.broadcast.emit('message', payload);
}
}
5. Ajout du module au AppModule
// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ChatModule } from './chat/chat.module';
@Module({
imports: [ChatModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
6. Création d'un service pour la gestion des messages (optionnel)
// src/chat/chat.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class ChatService {
private readonly messages: string[] = [];
findAll(): string[] {
return this.messages;
}
add(message: string): void {
this.messages.push(message);
}
}
7. Mise à jour du Gateway pour utiliser le service (optionnel)
// src/chat/chat.gateway.ts
import {
WebSocketGateway,
SubscribeMessage,
MessageBody,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { ChatService } from './chat.service';
@WebSocketGateway()
export class ChatGateway {
constructor(private readonly chatService: ChatService) {}
@SubscribeMessage('message')
handleMessage(client: Socket, payload: any): void {
this.chatService.add(payload);
client.broadcast.emit('message', payload);
}
handleConnection(client: Socket, ...args: any[]) {
client.emit('messages', this.chatService.findAll());
}
}
8. Ajout du service au module
// src/chat/chat.module.ts
import { Module } from '@nestjs/common';
import { ChatGateway } from './chat.gateway';
import { ChatService } from './chat.service';
@Module({
providers: [ChatGateway, ChatService],
})
export class ChatModule {}
9. Création du contrôleur pour la gestion des connexions
// src/chat/chat.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ChatGateway } from './chat.gateway';
@Controller('chat')
export class ChatController {
constructor(private readonly chatGateway: ChatGateway) {}
@Get()
broadcastMessage(): void {
this.chatGateway.server.emit('message', 'Hello, world!');
}
}
10. Ajout du contrôleur au module
// src/chat/chat.module.ts
import { Module } from '@nestjs/common';
import { ChatGateway } from './chat.gateway';
import { ChatService } from './chat.service';
import { ChatController } from './chat.controller';
@Module({
providers: [ChatGateway, ChatService],
controllers: [ChatController],
})
export class ChatModule {}
Erreurs frequentes et debugging
1. Connexion échoue : Error: listen EADDRINUSE
Code incorrect :
const server = http.createServer(app);
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
Code correct :
const server = http.createServer(app);
process.on('SIGINT', () => {
server.close(() => {
process.exit(0);
});
});
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
2. Émission d'événement : Error: Unknown event 'message'
Code incorrect :
client.emit('messages', message);
Code correct :
client.broadcast.emit('message', message);
3. Connexion au serveur WebSocket : Refused to connect because it violates the following Content Security Policy directive: "upgrade-insecure-requests"
Code incorrect :
<script src="http://localhost:3000/socket.io/socket.io.js"></script>
Code correct :
<script src="https://localhost:3000/socket.io/socket.io.js"></script>
Pour aller plus loin
1. Authentification et authorisation avec JWT
L'utilisation de JSON Web Tokens (JWT) pour l'authentification et l'autorisation des utilisateurs.
2. Gestion des événements personnalisés
Création d'événements personnalisés pour des fonctionnalités plus spécifiques.
3. Utilisation de Redis pour la mise en cache
Utilisation de Redis pour améliorer les performances en stockant les données dans un cache distribué.
Défi pratique à réaliser seul
Créez une application de vote simple où plusieurs utilisateurs peuvent voter pour des options et voir les résultats en temps réel. Utilisez NestJS, Socket.IO, et un service pour gérer les votes.
Bon codage!