Aflați cum să creați un API de chat în timp real, valorificând puterea WebSockets folosind NestJS.

NestJS este un cadru popular pentru construirea de aplicații pe server cu Node.js. Cu suportul său pentru WebSockets, NestJS este potrivit pentru dezvoltarea aplicațiilor de chat în timp real.

Deci, ce sunt WebSockets și cum puteți crea o aplicație de chat în timp real în NestJS?

Ce sunt WebSocket-urile?

WebSocket-urile sunt un protocol pentru comunicare persistentă, în timp real și bidirecțională între un client și un server.

Spre deosebire de HTTP, unde o conexiune este închisă atunci când un ciclu de solicitare este finalizat între client și server, o conexiune WebSocket este menținută deschisă și nu se închide chiar și după ce a fost returnat un răspuns pentru a cerere.

Imaginea de mai jos este o vizualizare a modului în care funcționează o comunicare WebSocket între un server și un client:

Pentru a stabili o comunicare bidirecțională, clientul trimite o cerere de strângere de mână WebSocket către server. Antetele cererii conțin o cheie WebSocket securizată (

instagram viewer
Sec-WebSocket-Key), si un Upgrade: WebSocket antet care împreună cu Conexiune: Upgrade antetul îi spune serverului să actualizeze protocolul de la HTTP la WebSocket și să mențină conexiunea deschisă. Învățând despre WebSockets în JavaScript ajută la înțelegerea conceptului și mai bine.

Crearea unui API de chat în timp real utilizând modulul NestJS WebSocket

Node.js oferă două implementări majore WebSockets. Primul este ws care implementează WebSocket-uri goale. Iar al doilea este socket.io, care oferă mai multe funcții de nivel înalt.

NestJS are module pentru ambele socket.io și ws. Acest articol folosește socket.io modul pentru caracteristicile WebSocket ale aplicației exemplu.

Codul folosit în acest proiect este disponibil în a Depozitul GitHub. Se recomandă să-l clonați local pentru a înțelege mai bine structura directorului și pentru a vedea cum toate codurile interacționează între ele.

Configurarea și instalarea proiectului

Deschideți terminalul și generați o nouă aplicație NestJS folosind cuib nou comanda (de ex. cuib noua aplicație de chat). Comanda generează un nou director care conține fișierele de proiect. Acum sunteți gata să începeți procesul de dezvoltare.

Configurați o conexiune MongoDB

Pentru a persista mesajele de chat în aplicație, aveți nevoie de o bază de date. Acest articol folosește baza de date MongoDB pentru aplicația noastră NestJS, iar cel mai simplu mod de a alerga este să configurați un cluster MongoDB în cloud și obțineți adresa URL MongoDB. Copiați URL-ul și stocați-l ca MONGO_URI variabilă în dvs .env fişier.

De asemenea, veți avea nevoie de Mongoose mai târziu când faceți interogări către MongoDB. Instalați-l rulând npm instalează mangusta în terminalul dvs.

În src folder, creați un fișier numit mongo.config.ts și inserați următorul cod în el.

import { registerAs } din„@nestjs/config”;

/**
* Configurarea conexiunii bazei de date Mongo
*/

exportMod implicit registerAs(„mongodb”, () => {
const { MONGO_URI } = process.env; // din fișierul .env
întoarcere {
uri:`${MONGO_URI}`,
};
});

Proiectul tău principale.ts fișierul ar trebui să arate așa:

import { NestFactory } din„@nestjs/core”;
import { AppModule } din„./app.module”;
import * la fel de cookieParser din„cookie-parser”
import cască din'cască'
import { Logger, ValidationPipe } din„@nestjs/common”;
import { setupSwagger } din„./utils/swagger”;
import { HttpExceptionFilter } din„./filters/http-exception.filter”;

asincronfuncţiebootstrap() {
const aplicație = asteapta NestFactory.create (AppModule, { cors: Adevărat });
app.enableCors({
origine: '*',
acreditări: Adevărat
})
app.use (cookieParser())
app.useGlobalPipes(
nou ValidationPipe({
lista albă: Adevărat
})
)
const logger = nou Logger('Principal')

app.setGlobalPrefix(„api/v1”)
app.useGlobalFilters(nou HttpExceptionFilter());

setupSwagger (aplicație)
app.use (casca())

asteapta app.listen (AppModule.port)

// înregistrează documente
const baseUrl = AppModule.getBaseUrl (aplicație)
const url = `http://${baseUrl}:${AppModule.port}`
logger.log(`Documentația API disponibilă la ${url}/docs`);
}
bootstrap();

Construirea modulului de chat

Pentru a începe cu funcția de chat în timp real, primul pas este să instalați pachetele NestJS WebSockets. Acest lucru se poate face rulând următoarea comandă în terminal.

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

După instalarea pachetelor, trebuie să generați modulul de chat rulând următoarele comenzi

chat-uri module nest g
conversații cu controlerul nest g
chat-uri de serviciu nest g

Odată ce ați terminat de generat modulul, următorul pas este să creați o conexiune WebSockets în NestJS. Creeaza o chat.gateway.ts dosar în interiorul chaturi folder, aici este implementat gateway-ul care trimite și primește mesaje.

Lipiți următorul cod în chat.gateway.ts.

import {
Conținutul mesajului,
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
} din„@nestjs/websockets”;
import { Server } din„socket.io”;

@WebSocketGateway()
exportclasăChatGateway{
@WebSocketServer()
server: Server;
// asculta evenimente send_message
@SubscribeMessage('Trimite mesaj')
listenForMessages(@MessageBody() mesaj: șir) {
acest.server.sockets.emit(„receive_message”, mesaj);
}
}

Autentificarea utilizatorilor conectați

Autentificarea este o parte esențială a aplicațiilor web și nu este diferită pentru o aplicație de chat. Funcția de autentificare a conexiunilor client la socket se găsește în chats.service.ts așa cum se arată aici:

@Injectabil()
exportclasăChatsService{
constructor(Serviciul de auth privat: AuthService) {}

asincron getUserFromSocket (socket: Socket) {
lăsa auth_token = socket.handshake.headers.authorization;
// obțineți jetonul în sine fără „Purtător”
auth_token = auth_token.split(' ')[1];

const utilizator = acest.authService.getUserFromAuthenticationToken(
auth_token
);

dacă (!utilizator) {
aruncanou WsException(„Acreditări nevalide”.);
}
întoarcere utilizator;
}
}

The getUserFromSocket metode de utilizare getUserFromAuthenticationToken pentru a obține utilizatorul conectat în prezent din simbolul JWT prin extragerea jetonului Bearer. The getUserFromAuthenticationToken funcția este implementată în auth.service.ts fișier așa cum se arată aici:

public asincron getUserFromAuthenticationToken (indicativ: șir) {
const sarcină utilă: JwtPayload = acest.jwtService.verify (indicativ, {
secret: acest.configService.get(„JWT_ACCESS_TOKEN_SECRET”),
});

const userId = payload.sub

dacă (ID-ul de utilizator) {
întoarcereacest.usersService.findById (userId);
}
}

Priza curentă este transmisă ca parametru către getUserFromSocket cand handleConnection Metodă de ChatGateway implementează OnGatewayConnection interfață. Acest lucru face posibilă primirea de mesaje și informații despre utilizatorul conectat în prezent.

Codul de mai jos demonstrează acest lucru:

// chat.gateway.ts
@WebSocketGateway()
exportclasăChatGatewayunelteOnGatewayConnection{
@WebSocketServer()
server: Server;

constructor(serviciu de chat privat: Serviciu de chat) {}

asincron mânerConexiune (priză: priză) {
asteaptaacest.chatsService.getUserFromSocket (socket)
}

@SubscribeMessage('Trimite mesaj')
asincron listenForMessages(@MessageBody() mesaj: șir, @ConnectedSocket() socket: Socket) {

const utilizator = asteaptaacest.chatsService.getUserFromSocket (socket)
acest.server.sockets.emit(„receive_message”, {
mesaj,
utilizator
});
}
}

Puteți face referire la fișierele implicate în sistemul de autentificare de mai sus în Depozitul GitHub pentru a vedea codurile complete (inclusiv importurile), pentru o mai bună înțelegere a implementării.

Chaturi persistente în baza de date

Pentru ca utilizatorii să-și vadă istoricul mesajelor, aveți nevoie de o schemă pentru a stoca mesajele. Creați un fișier nou numit mesaj.schema.ts și inserați codul de mai jos în el (nu uitați să importați schema utilizatorului sau verificați depozitul pentru unul).

import { Utilizator } din„./../users/schemas/user.schema”;
import { Prop, Schema, SchemaFactory } din„@nestjs/mongoose”;
import mangusta, { Document } din"mangustă";

export tip MessageDocument = Mesaj & Document;

@Schemă({
toJSON: {
getters: Adevărat,
virtuale: Adevărat,
},
marcaje de timp: Adevărat,
})
exportclasăMesaj{
@Recuzită({ necesar: Adevărat, unic: Adevărat })
mesaj: șir

@Recuzită({ tip: mangusta. Schemă. Tipuri. ObjectId, ref: 'Utilizator' })
utilizator: utilizator
}

const MessageSchema = SchemaFactory.createForClass (Mesaj)

export { MessageSchema };

Mai jos este o implementare a serviciilor pentru a crea un mesaj nou și a primi toate mesajele chats.service.ts.

import { Mesaj, MessageDocument } din„./message.schema”; 
import { Priză } din„socket.io”;
import { AuthService } din„./../auth/auth.service”;
import { injectabil } din„@nestjs/common”;
import { WsException } din„@nestjs/websockets”;
import { InjectModel } din„@nestjs/mongoose”;
import { Model } din'mangustă';
import { MessageDto } din„./dto/message.dto”;

@Injectabil()
exportclasăChatsService{
constructor(private authService: AuthService, @InjectModel (Message.name) mesaj privat Model: Model) {}
...
asincron createMessage (mesaj: MessageDto, ID-ul de utilizator: șir) {
const newMessage = nouacest.messageModel({...message, userId})
asteapta mesaj nou.salvare
întoarcere mesaj nou
}
asincron getAllMessages() {
întoarcereacest.messageModel.find().populate('utilizator')
}
}

The MessageDto este implementat în a mesaj.dto.ts dosar în dto folderul în chaturi director. Îl puteți găsi și în depozit.

Trebuie să adăugați mesaj model și schema la lista de importuri în chats.module.ts.

import { Mesaj, MessageSchema } din„./message.schema”;
import { Modulul } din„@nestjs/common”;
import { ChatGateway } din„./chats.gateway”;
import { ChatsService } din„./chats.service”;
import { MongooseModule } din„@nestjs/mongoose”;

@Modul({
importuri: [MongooseModule.forFeature([
{ Nume: Nume.Mesaj, schemă: MessageSchema }
])],
controlere: [],
furnizori: [ChatsService, ChatGateway]
})
exportclasăChatsModule{}

În cele din urmă, cel primiți_toate_mesajele handler de evenimente este adăugat la ChatGateway clasa in chat.gateway.ts asa cum se vede in urmatorul cod:

// importuri...

@WebSocketGateway()
exportclasăChatGatewayunelteOnGatewayConnection{
...

@SubscribeMessage(„get_all_messages”)
asincron getAllMessages(@ConnectedSocket() socket: Socket) {

asteaptaacest.chatsService.getUserFromSocket (socket)
const mesaje = asteaptaacest.chatsService.getAllMessages()

acest.server.sockets.emit(„receive_message”, mesaje);

întoarcere mesaje
}
}

Când un client (utilizator) conectat emite primiți_toate_mesajele eveniment, toate mesajele lor vor fi preluate și când emit Trimite mesaj, un mesaj este creat și stocat în baza de date, apoi trimis tuturor celorlalți clienți conectați.

După ce ați terminat toți pașii de mai sus, puteți începe să utilizați aplicația npm run start: devși testați-l cu un client WebSocket precum Postman.

Crearea de aplicații în timp real cu NestJS

Deși există și alte tehnologii pentru construirea de sisteme în timp real, WebSocket-urile sunt foarte populare și ușor de implementat în multe cazuri și sunt cea mai bună opțiune pentru aplicațiile de chat.

Aplicațiile în timp real nu se limitează doar la aplicațiile de chat, alte exemple includ streaming video sau aplicații de apelare și aplicații meteo live, iar NestJS oferă instrumente excelente pentru construirea în timp real aplicații.