Erfahren Sie, wie Sie mit NestJS eine Echtzeit-Chat-API erstellen und dabei die Leistungsfähigkeit von WebSockets nutzen.

NestJS ist ein beliebtes Framework zum Erstellen serverseitiger Anwendungen mit Node.js. Mit seiner Unterstützung für WebSockets eignet sich NestJS gut für die Entwicklung von Echtzeit-Chat-Anwendungen.

Was sind WebSockets und wie können Sie eine Echtzeit-Chat-App in NestJS erstellen?

Was sind WebSockets?

WebSockets sind ein Protokoll für die dauerhafte, bidirektionale Echtzeitkommunikation zwischen einem Client und einem Server.

Anders als bei HTTP, wo eine Verbindung geschlossen wird, wenn ein Anforderungszyklus zwischen Client und Server abgeschlossen ist, Eine WebSocket-Verbindung bleibt offen und wird nicht geschlossen, selbst nachdem eine Antwort für a zurückgegeben wurde Anfrage.

Das Bild unten ist eine Visualisierung, wie eine WebSocket-Kommunikation zwischen einem Server und einem Client funktioniert:

Um eine bidirektionale Kommunikation herzustellen, sendet der Client eine WebSocket-Handshake-Anfrage an den Server. Die Anforderungsheader enthalten einen sicheren WebSocket-Schlüssel (

instagram viewer
Sec-WebSocket-Schlüssel), und ein Upgrade: WebSocket Header, der zusammen mit dem Verbindung: Upgrade Der Header weist den Server an, das Protokoll von HTTP auf WebSocket zu aktualisieren und die Verbindung offen zu halten. Lernen über WebSockets in JavaScript hilft, das Konzept noch besser zu verstehen.

Erstellen einer Echtzeit-Chat-API mithilfe des NestJS-WebSocket-Moduls

Node.js bietet zwei wichtige WebSockets-Implementierungen. Das erste ist ws welches nackte WebSockets implementiert. Und das zweite ist socket.io, das mehr High-Level-Funktionen bietet.

NestJS verfügt über Module für beides socket.io Und ws. Dieser Artikel verwendet die socket.io Modul für die WebSocket-Funktionen der Beispielanwendung.

Der in diesem Projekt verwendete Code ist in a verfügbar GitHub-Repository. Es wird empfohlen, es lokal zu klonen, um die Verzeichnisstruktur besser zu verstehen und zu sehen, wie alle Codes miteinander interagieren.

Projekteinrichtung und Installation

Öffnen Sie Ihr Terminal und generieren Sie eine neue NestJS-App mit Nest neu Befehl (z.B. Nest neue Chat-App). Der Befehl generiert ein neues Verzeichnis, das die Projektdateien enthält. Jetzt können Sie mit dem Entwicklungsprozess beginnen.

Richten Sie eine MongoDB-Verbindung ein

Um die Chat-Nachrichten in der Anwendung zu speichern, benötigen Sie eine Datenbank. Dieser Artikel verwendet die MongoDB-Datenbank für unsere NestJS-Anwendung, und der einfachste Weg, zum Laufen zu kommen, ist Richten Sie einen MongoDB-Cluster in der Cloud ein und erhalten Sie Ihre MongoDB-URL. Kopieren Sie die URL und speichern Sie sie als MONGO_URI Variable in Ihrem .env Datei.

Sie benötigen Mongoose auch später, wenn Sie Anfragen an MongoDB stellen. Installieren Sie es, indem Sie es ausführen npm mongoose installieren in Ihrem Terminal.

Im src Ordner, erstellen Sie eine Datei mit dem Namen mongo.config.ts und fügen Sie den folgenden Code ein.

importieren {registerAs} aus'@nestjs/config';

/**
* Mongo-Datenbankverbindungskonfiguration
*/

ExportStandard registrierenAs('mongodb', () => {
const { MONGO_URI } = process.env; // aus der .env-Datei
zurückkehren {
uri:`${MONGO_URI}`,
};
});

Ihr Projekt main.ts Die Datei sollte so aussehen:

importieren {NestFactory} aus'@nestjs/core';
importieren { AppModule } aus'./app.module';
importieren * als cookieParser aus'Cookie-Parser'
importieren Helm aus'Helm'
importieren { Logger, ValidationPipe } aus'@nestjs/common';
importieren { setupSwagger } aus'./utils/swagger';
importieren { HttpExceptionFilter } aus'./filters/http-Exception.filter';

asynchronFunktionBootstrap() {
const app = erwarten NestFactory.create (AppModule, { kors: WAHR });
app.enableCors({
Herkunft: '*',
Referenzen: WAHR
})
app.use (cookieParser())
app.useGlobalPipes(
neu ValidationPipe({
Whitelist: WAHR
})
)
const Logger = neu Logger('Hauptsächlich')

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

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

erwarten app.listen (AppModule.port)

// Protokolldokumente
const baseUrl = AppModule.getBaseUrl (App)
const URL = `http://${baseUrl}:${AppModule.port}`
logger.log(„API-Dokumentation verfügbar unter ${url}/docs`);
}
Bootstrap();

Erstellen des Chat-Moduls

Um mit der Echtzeit-Chat-Funktion zu beginnen, besteht der erste Schritt darin, die NestJS WebSockets-Pakete zu installieren. Dies kann durch Ausführen des folgenden Befehls im Terminal erfolgen.

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

Nach der Installation der Pakete müssen Sie das Chats-Modul generieren, indem Sie die folgenden Befehle ausführen

Nest-G-Modul-Chats
Nest-G-Controller-Chats
Nest G-Service-Chats

Sobald das Modul generiert ist, besteht der nächste Schritt darin, eine WebSockets-Verbindung in NestJS zu erstellen. Ein... kreieren chat.gateway.ts Datei innerhalb der Chats Ordner, hier ist das Gateway implementiert, das Nachrichten sendet und empfängt.

Fügen Sie den folgenden Code ein chat.gateway.ts.

importieren {
Nachrichtentext,
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
} aus'@nestjs/websockets';
importieren { Server } aus'socket.io';

@WebSocketGateway()
ExportKlasseChatGateway{
@WebSocketServer()
Server: Server;
// Auf send_message-Ereignisse achten
@SubscribeMessage('Nachricht senden')
listenForMessages(@MessageBody() message: string) {
Das.server.sockets.emit('erhalte Nachricht', Nachricht);
}
}

Authentifizieren verbundener Benutzer

Die Authentifizierung ist ein wesentlicher Bestandteil von Webanwendungen, und das ist bei einer Chat-Anwendung nicht anders. Die Funktion zur Authentifizierung von Client-Verbindungen zum Socket finden Sie in chats.service.ts wie hier gezeigt:

@Injectable()
ExportKlasseChatsService{
Konstrukteur(privater AuthService: AuthService) {}

asynchron getUserFromSocket (Socket: Socket) {
lassen auth_token = socket.handshake.headers.authorization;
// den Token selbst ohne „Bearer“ erhalten
auth_token = auth_token.split(' ')[1];

const Benutzer = Das.authService.getUserFromAuthenticationToken(
auth_token
);

Wenn (!Benutzer) {
Wurfneu WsException(„Ungültige Anmeldeinformationen.“);
}
zurückkehren Benutzer;
}
}

Der getUserFromSocket Methode verwendet getUserFromAuthenticationToken um den aktuell angemeldeten Benutzer aus dem JWT-Token abzurufen, indem das Bearer-Token extrahiert wird. Der getUserFromAuthenticationToken Funktion ist in der implementiert auth.service.ts Datei wie hier gezeigt:

öffentlich asynchron getUserFromAuthenticationToken (token: string) {
const Nutzlast: JwtPayload = Das.jwtService.verify (Token, {
Geheimnis: Das.configService.get('JWT_ACCESS_TOKEN_SECRET'),
});

const userId = payload.sub

Wenn (Benutzer-ID) {
zurückkehrenDas.usersService.findById (Benutzer-ID);
}
}

Der aktuelle Socket wird als Parameter an übergeben getUserFromSocket wenn das handleConnection Methode von ChatGateway implementiert die OnGatewayConnection Schnittstelle. Dadurch ist es möglich, Nachrichten und Informationen über den aktuell verbundenen Benutzer zu empfangen.

Der folgende Code demonstriert dies:

// chat.gateway.ts
@WebSocketGateway()
ExportKlasseChatGatewayimplementiertOnGatewayConnection{
@WebSocketServer()
Server: Server;

Konstrukteur(privater ChatsService: ChatsService) {}

asynchron handleConnection (Socket: Socket) {
erwartenDas.chatsService.getUserFromSocket (Socket)
}

@SubscribeMessage('Nachricht senden')
asynchron listenForMessages(@MessageBody() message: string, @ConnectedSocket() socket: Socket) {

const Benutzer = erwartenDas.chatsService.getUserFromSocket (Socket)
Das.server.sockets.emit('erhalte Nachricht', {
Nachricht,
Benutzer
});
}
}

Sie können auf die oben im Authentifizierungssystem beteiligten Dateien verweisen GitHub-Repository um die vollständigen Codes (einschließlich Importe) anzuzeigen, um die Implementierung besser zu verstehen.

Persistente Chats in der Datenbank

Damit Benutzer ihren Nachrichtenverlauf sehen können, benötigen Sie ein Schema zum Speichern von Nachrichten. Erstellen Sie eine neue Datei mit dem Namen message.schema.ts und fügen Sie den folgenden Code ein (denken Sie daran, Ihren zu importieren). Benutzerschema oder schauen Sie sich das Repository an).

importieren { Benutzer } aus'./../users/schemas/user.schema';
importieren { Prop, Schema, SchemaFactory } aus„@nestjs/mongoose“;
importieren Mungo, { Dokument } aus"Mungo";

Export type MessageDocument = Nachricht & Dokument;

@Schema({
toJSON: {
Getter: WAHR,
Virtuelle: WAHR,
},
Zeitstempel: WAHR,
})
ExportKlasseNachricht{
@Stütze({ erforderlich: WAHR, einzigartig: WAHR })
Nachricht: Zeichenfolge

@Stütze({ Typ: Mungo. Schema. Typen. Objekt Identifikation, ref: 'Benutzer' })
Benutzer: Benutzer
}

const MessageSchema = SchemaFactory.createForClass (Nachricht)

Export { MessageSchema };

Nachfolgend finden Sie eine Implementierung von Diensten zum Erstellen einer neuen Nachricht und zum Empfangen aller Nachrichten chats.service.ts.

importieren { Nachricht, MessageDocument } aus'./message.schema'; 
importieren { Steckdose } aus'socket.io';
importieren { AuthService } aus'./../auth/auth.service';
importieren {Injizierbar} aus'@nestjs/common';
importieren { WsException } aus'@nestjs/websockets';
importieren {InjectModel} aus'@nestjs/mongoose';
importieren { Modell } aus'Mungo';
importieren { MessageDto } aus'./dto/message.dto';

@Injectable()
ExportKlasseChatsService{
Konstrukteur(privater AuthService: AuthService, @InjectModel (Message.name) privates messageModel: Model) {}
...
asynchron createMessage (Nachricht: MessageDto, Benutzer-ID: Zeichenfolge) {
const newMessage = neuDas.messageModel({...message, userId})
erwarten newMessage.save
zurückkehren neue Nachricht
}
asynchron getAllMessages() {
zurückkehrenDas.messageModel.find().populate('Benutzer')
}
}

Der MessageDto wird in a umgesetzt message.dto.ts Datei in der dto Ordner im Chats Verzeichnis. Sie finden es auch im Repository.

Sie müssen das hinzufügen Nachricht Modell und Schema zur Liste der Importe hinzufügen chats.module.ts.

importieren { Nachricht, MessageSchema } aus'./message.schema';
importieren { Modul } aus'@nestjs/common';
importieren { ChatGateway } aus'./chats.gateway';
importieren { ChatsService } aus'./chats.service';
importieren { MongooseModule } aus'@nestjs/mongoose';

@Modul({
Importe: [MongooseModule.forFeature([
{ Name: Nachrichtenname, Schema: MessageSchema }
])],
Controller: [],
Anbieter: [ChatsService, ChatGateway]
})
ExportKlasseChatsModule{}

Endlich, das get_all_messages Der Ereignishandler wird hinzugefügt ChatGateway Klasse in chat.gateway.ts wie im folgenden Code zu sehen:

// importiert...

@WebSocketGateway()
ExportKlasseChatGatewayimplementiertOnGatewayConnection{
...

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

erwartenDas.chatsService.getUserFromSocket (Socket)
const Nachrichten = erwartenDas.chatsService.getAllMessages()

Das.server.sockets.emit('erhalte Nachricht', Mitteilungen);

zurückkehren Mitteilungen
}
}

Wenn ein verbundener Client (Benutzer) das ausgibt get_all_messages Bei diesem Ereignis werden alle ihre Nachrichten abgerufen und wann sie gesendet werden Nachricht senden, Es wird eine Nachricht erstellt, in der Datenbank gespeichert und dann an alle anderen verbundenen Clients gesendet.

Sobald Sie alle oben genannten Schritte ausgeführt haben, können Sie Ihre Anwendung mit starten npm-Laufstart: dev, und testen Sie es mit einem WebSocket-Client wie Postman.

Erstellen von Echtzeitanwendungen mit NestJS

Obwohl es andere Technologien zum Aufbau von Echtzeitsystemen gibt, sind WebSockets in vielen Fällen sehr beliebt und einfach zu implementieren und die beste Option für Chat-Anwendungen.

Echtzeitanwendungen beschränken sich nicht nur auf Chat-Anwendungen, andere Beispiele umfassen Video-Streaming oder Aufrufanwendungen und Live-Wetteranwendungen, und NestJS bietet großartige Tools für die Erstellung in Echtzeit Apps.