Accueil Technologie
Construire une API de chat en temps réel en utilisant les WebSockets dans NestJS

Construire une API de chat en temps réel en utilisant les WebSockets dans NestJS

NestJS est un framework populaire pour la création d’applications côté serveur avec Node.js. Grâce à sa prise en charge des WebSockets, NestJS est bien adapté au développement d’applications de chat en temps réel.


Qu’est-ce que les WebSockets et comment construire une application de chat en temps réel avec NestJS ?


Qu’est-ce que les WebSockets ?

Les WebSockets sont un protocole de communication persistante, en temps réel et bidirectionnelle entre un client et un serveur.

Contrairement au protocole HTTP où une connexion est fermée lorsque le cycle de requête entre le client et le serveur est terminé, une connexion WebSocket reste ouverte et ne se ferme pas même après qu’une réponse a été renvoyée pour une requête.

L’image ci-dessous illustre le fonctionnement d’une communication WebSocket entre un serveur et un client :

Visualisation de la communication client-serveur par WebSocket

Pour établir une communication bidirectionnelle, le client envoie une demande de prise de contact WebSocket au serveur. Les en-têtes de la requête contiennent une clé WebSocket sécurisée (Sec-WebSocket-Key), et un Mise à jour : WebSocket qui, avec l’en-tête Connexion : Mise à niveau indique au serveur de passer du protocole HTTP au protocole WebSocket et de maintenir la connexion ouverte. L’apprentissage des WebSockets en JavaScript permet de mieux comprendre le concept.

Construire une API de chat en temps réel en utilisant le module WebSocket de NestJS

Node.js fournit deux implémentations principales de WebSockets. La première est ws qui implémente les WebSockets nus. La seconde est socket.io, qui fournit des fonctionnalités de haut niveau.

NestJS possède des modules pour socket.io et ws. Cet article utilise le module socket.io pour les fonctionnalités WebSocket de l’application exemple.

Le code utilisé dans ce projet est disponible dans un dépôt GitHub. Il est recommandé de le cloner localement pour mieux comprendre la structure des répertoires et voir comment tous les codes interagissent les uns avec les autres.

Configuration et installation du projet

Ouvrez votre terminal et générez une nouvelle application NestJS en utilisant la commande nest new (par exemple nicher une nouvelle application de chat). La commande génère un nouveau répertoire contenant les fichiers du projet. Vous êtes maintenant prêt à lancer le processus de développement.

Configurer une connexion MongoDB

Pour conserver les messages de chat dans l’application, vous avez besoin d’une base de données. Cet article utilise la base de données MongoDB pour notre application NestJS, et la façon la plus simple de commencer est de mettre en place un cluster MongoDB dans le cloud et d’obtenir votre URL MongoDB. Copiez l’URL et stockez-la en tant que fichier MONGO_URI dans votre .env fichier.

Vous aurez également besoin de Mongoose plus tard lorsque vous ferez des requêtes à MongoDB. Installez-le en exécutant npm install mongoose dans votre terminal.

Dans le src créer un fichier appelé mongo.config.ts et y coller le code suivant.

 import { registerAs } from '@nestjs/config';

/**
* Mongo database connection config
*/

export default registerAs('mongodb', () => {
  const { MONGO_URI } = process.env; // from .env file
  return {
    uri:`${MONGO_URI}`,
  };
});

Le code main.ts devrait ressembler à ceci :

 import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as cookieParser from 'cookie-parser'
import helmet from 'helmet'
import { Logger, ValidationPipe } from '@nestjs/common';
import { setupSwagger } from './utils/swagger';
import { HttpExceptionFilter } from './filters/http-exception.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { cors: true });
  app.enableCors({
    origin: '*',
    credentials: true
  })
  app.use(cookieParser())
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true
    })
  )
  const logger = new Logger('Main')

  app.setGlobalPrefix('api/v1')
  app.useGlobalFilters(new HttpExceptionFilter());

  setupSwagger(app)
  app.use(helmet())

  await app.listen(AppModule.port)

  // log docs
  const baseUrl = AppModule.getBaseUrl(app)
  const url = `http://${baseUrl}:${AppModule.port}`
  logger.log(`API Documentation available at ${url}/docs`);
}
bootstrap();

Construction du module de chat

Pour commencer à utiliser la fonction de chat en temps réel, la première étape consiste à installer les paquets NestJS WebSockets. Cela peut être fait en exécutant la commande suivante dans le terminal.

 npm install @nestjs/websockets @nestjs/platform-socket.io @types/socket.io 

Après avoir installé les paquets, vous devez générer le module chats en exécutant les commandes suivantes

 nest g module chats
nest g controller chats
nest g service chats

Une fois le module généré, l’étape suivante consiste à créer une connexion WebSockets dans NestJS. Créez une connexion chat.gateway.ts à l’intérieur du fichier chats c’est ici que la passerelle qui envoie et reçoit les messages est mise en œuvre.

Collez le code suivant dans chat.gateway.ts.

 import {
    MessageBody,
    SubscribeMessage,
    WebSocketGateway,
    WebSocketServer,
  } from '@nestjs/websockets';
import { Server } from 'socket.io';

@WebSocketGateway()
export class ChatGateway {
    @WebSocketServer()
    server: Server;
    // listen for send_message events
    @SubscribeMessage('send_message')
    listenForMessages(@MessageBody() message: string) {
      this.server.sockets.emit('receive_message', message);
    }
}

Authentification des utilisateurs connectés

L’authentification est un élément essentiel des applications web, et il en va de même pour une application de chat. La fonction permettant d’authentifier les connexions du client à la socket se trouve dans le fichier chats.service.ts comme indiqué ici :

 @Injectable()
export class ChatsService {
    constructor(private authService: AuthService) {}

    async getUserFromSocket(socket: Socket) {
        let auth_token = socket.handshake.headers.authorization;
        // get the token itself without "Bearer"
        auth_token = auth_token.split(' ')[1];

        const user = this.authService.getUserFromAuthenticationToken(
            auth_token
        );

        if (!user) {
            throw new WsException('Invalid credentials.');
        }
        return user;
    }
}

Le getUserFromSocket La méthode utilise getUserFromAuthenticationToken pour obtenir l’utilisateur actuellement connecté à partir du jeton JWT en extrayant le jeton Bearer. La fonction getUserFromAuthenticationToken est implémentée dans la fonction auth.service.ts comme indiqué ici :

 public async getUserFromAuthenticationToken(token: string) {
        const payload: JwtPayload = this.jwtService.verify(token, {
          secret: this.configService.get('JWT_ACCESS_TOKEN_SECRET'),
        });

        const userId = payload.sub

        if (userId) {
            return this.usersService.findById(userId);
        }
      }

La prise courante est passée en paramètre à getUserFromSocket lorsque la fonction handleConnection méthode de ChatGateway implémente la fonction OnGatewayConnection . Elle permet de recevoir des messages et des informations sur l’utilisateur actuellement connecté.

Le code ci-dessous en fait la démonstration :

 // chat.gateway.ts
@WebSocketGateway()
export class ChatGateway implements OnGatewayConnection {
    @WebSocketServer()
    server: Server;

    constructor(private chatsService: ChatsService) {}

    async handleConnection(socket: Socket) {
        await this.chatsService.getUserFromSocket(socket)
    }

    @SubscribeMessage('send_message')
    async listenForMessages(@MessageBody() message: string, @ConnectedSocket() socket: Socket) {

        const user = await this.chatsService.getUserFromSocket(socket)
        this.server.sockets.emit('receive_message', {
            message,
            user
        });
    }
}

Vous pouvez référencer les fichiers impliqués dans le système d’authentification ci-dessus dans le dépôt GitHub pour voir les codes complets (y compris les importations), pour une meilleure compréhension de la mise en œuvre.

Persistance des chats dans la base de données

Pour que les utilisateurs puissent voir l’historique de leurs messages, vous avez besoin d’un schéma pour stocker les messages. Créez un nouveau fichier appelé message.schema.ts et collez-y le code ci-dessous (n’oubliez pas d’importer votre fichier schéma d’utilisateur ou consulter le référentiel pour en trouver un).

 import { User } from './../users/schemas/user.schema';
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import mongoose, { Document } from "mongoose";

export type MessageDocument = Message & Document;

@Schema({
    toJSON: {
        getters: true,
        virtuals: true,
    },
    timestamps: true,
})
export class Message {
    @Prop({ required: true, unique: true })
    message: string

    @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' })
    user: User
}

const MessageSchema = SchemaFactory.createForClass(Message)

export { MessageSchema };

Voici une implémentation de services permettant de créer un nouveau message et de récupérer tous les messages dans chats.service.ts.

 import { Message, MessageDocument } from './message.schema'; 
import { Socket } from 'socket.io';
import { AuthService } from './../auth/auth.service';
import { Injectable } from '@nestjs/common';
import { WsException } from '@nestjs/websockets';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { MessageDto } from './dto/message.dto';

@Injectable()
 export class ChatsService {
    constructor(private authService: AuthService, @InjectModel(Message.name) private messageModel: Model<MessageDocument>) {}
    ....
    async createMessage(message: MessageDto, userId: string) {
        const newMessage = new this.messageModel({...message, userId})
        await newMessage.save
       return newMessage
    }
    async getAllMessages() {
       return this.messageModel.find().populate('user')
    }
}

Les MessageDto est implémenté dans un message.dto.ts dans le fichier dto dans le dossier chats . Vous pouvez également le trouver dans le dépôt.

Vous devez ajouter l’élément message ajout du modèle et du schéma à la liste des importations en chats.module.ts.

 import { Message, MessageSchema } from './message.schema';
import { Module } from '@nestjs/common';
import { ChatGateway } from './chats.gateway';
import { ChatsService } from './chats.service';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [MongooseModule.forFeature([
    { name: Message.name, schema: MessageSchema }
  ])],
  controllers: [],
  providers: [ChatsService, ChatGateway]
})
export class ChatsModule {}

Enfin, le get_all_messages est ajouté au gestionnaire d’événements ChatGateway classe dans chat.gateway.ts comme le montre le code suivant :

 // imports...

@WebSocketGateway()
export class ChatGateway implements OnGatewayConnection {
    ....

    @SubscribeMessage('get_all_messages')
    async getAllMessages(@ConnectedSocket() socket: Socket) {

        await this.chatsService.getUserFromSocket(socket)
        const messages = await this.chatsService.getAllMessages()

        this.server.sockets.emit('receive_message', messages);

        return messages
    }
}

Lorsqu’un client connecté (utilisateur) émet la commande get_all_messages tous leurs messages seront récupérés, et lorsqu’ils émettront le message send_message, un message est créé et stocké dans la base de données, puis envoyé à tous les autres clients connectés.

Une fois que vous avez terminé toutes les étapes ci-dessus, vous pouvez démarrer votre application en utilisant npm run start:devet testez-le avec un client WebSocket comme Postman.

Construire des applications en temps réel avec NestJS

Bien qu’il existe d’autres technologies pour construire des systèmes en temps réel, les WebSockets sont très populaires et faciles à mettre en œuvre dans de nombreux cas, et ils constituent la meilleure option pour les applications de chat.

Les applications en temps réel ne se limitent pas aux applications de chat, d’autres exemples incluent les applications de streaming vidéo ou d’appel, et les applications de météo en direct, et NestJS fournit d’excellents outils pour la construction d’applications en temps réel.

Leave your vote

0 0 votes
Évaluation de l'article
S’abonner
Notification pour
guest
0 Commentaires
Le plus ancien
Le plus récent Le plus populaire
Commentaires en ligne
Afficher tous les commentaires

Log In

Forgot password?

Don't have an account? Register

Forgot password?

Enter your account data and we will send you a link to reset your password.

Your password reset link appears to be invalid or expired.

Log in

Privacy Policy

Add to Collection

No Collections

Here you'll find all collections you've created before.

0
Nous aimerions avoir votre avis, veuillez laisser un commentaire.x