Lär dig hur du bygger ett chatt-API i realtid som utnyttjar kraften hos WebSockets med NestJS.

NestJS är ett populärt ramverk för att bygga applikationer på serversidan med Node.js. Med sitt stöd för WebSockets är NestJS väl lämpat för att utveckla chattapplikationer i realtid.

Så, vad är WebSockets, och hur kan du bygga en chattapp i realtid i NestJS?

Vad är WebSockets?

WebSockets är ett protokoll för beständig, realtids- och tvåvägskommunikation mellan en klient och en server.

Till skillnad från i HTTP där en anslutning stängs när en begärandecykel är klar mellan klienten och servern, en WebSocket-anslutning hålls öppen och stängs inte ens efter att ett svar har returnerats för en begäran.

Bilden nedan är en visualisering av hur en WebSocket-kommunikation mellan en server och klient fungerar:

För att upprätta dubbelriktad kommunikation skickar klienten en WebSocket-handskakningsbegäran till servern. Begärans rubriker innehåller en säker WebSocket-nyckel (Sec-WebSocket-Key), och en Uppgradering: WebSocket

instagram viewer
header som tillsammans med Anslutning: Uppgradering header talar om för servern att uppgradera protokollet från HTTP till WebSocket och hålla anslutningen öppen. Lär sig om WebSockets i JavaScript hjälper till att förstå konceptet ännu bättre.

Bygga ett realtidschatt-API med hjälp av NestJS WebSocket-modulen

Node.js tillhandahåller två stora WebSockets-implementationer. Den första är ws som implementerar blotta WebSockets. Och den andra är socket.io, som ger fler funktioner på hög nivå.

NestJS har moduler för båda socket.io och ws. Den här artikeln använder socket.io modul för exempelapplikationens WebSocket-funktioner.

Koden som används i detta projekt är tillgänglig i en GitHub-förråd. Det rekommenderas att du klona det lokalt för att bättre förstå katalogstrukturen och se hur alla koder interagerar med varandra.

Projektuppsättning och installation

Öppna din terminal och generera en ny NestJS-app med hjälp av bo nytt kommando (t.ex. näst nya chatt-app). Kommandot genererar en ny katalog som innehåller projektfilerna. Nu är du redo att starta utvecklingsprocessen.

Konfigurera en MongoDB-anslutning

För att bevara chattmeddelandena i programmet behöver du en databas. Denna artikel använder MongoDB-databasen för vår NestJS-applikation, och det enklaste sättet att springa är att skapa ett MongoDB-kluster i molnet och få din MongoDB URL. Kopiera URL: en och lagra den som MONGO_URI variabel i din .env fil.

Du skulle också behöva Mongoose senare när du ställer frågor till MongoDB. Installera den genom att köra npm installera mongoose i din terminal.

I den src mapp, skapa en fil som heter mongo.config.ts och klistra in följande kod i den.

importera { registerAs } från'@nestjs/config';

/**
* Mongo databas anslutningskonfiguration
*/

exporterastandard registerAs('mongodb', () => {
konst { MONGO_URI } = process.env; // från .env-fil
lämna tillbaka {
uri:`${MONGO_URI}`,
};
});

Ditt projekt main.ts filen ska se ut så här:

importera { NestFactory } från'@nestjs/core';
importera { AppModule } från'./app.modul';
importera * som cookieParser från"cookie-parser"
importera hjälm från'hjälm'
importera { Logger, ValidationPipe } från'@nestjs/common';
importera { setupSwagger } från'./utils/swagger';
importera { HttpExceptionFilter } från'./filters/http-exception.filter';

asynkronfungerabootstrap() {
konst app = vänta NestFactory.create (AppModule, { cors: Sann });
app.enableCors({
ursprung: '*',
referenser: Sann
})
app.use (cookieParser())
app.useGlobalPipes(
ny ValidationPipe({
vitlista: Sann
})
)
konst logger = ny Logger('Main')

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

setupSwagger (app)
app.use (hjälm())

vänta app.listen (AppModule.port)

// log docs
konst baseUrl = AppModule.getBaseUrl (app)
konst url = `http://${baseUrl}:${AppModule.port}`
logger.log(`API-dokumentation tillgänglig på ${url}/docs`);
}
bootstrap();

Bygga chattmodulen

För att komma igång med chattfunktionen i realtid är det första steget att installera NestJS WebSockets-paketen. Detta kan göras genom att köra följande kommando i terminalen.

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

Efter att du har installerat paketen måste du skapa chattmodulen genom att köra följande kommandon

nest g module chattar
nest g controller chattar
nest g service chattar

När du har skapat modulen är nästa steg att skapa en WebSockets-anslutning i NestJS. Skapa en chat.gateway.ts fil inuti chattar mapp, det är här gatewayen som skickar och tar emot meddelanden implementeras.

Klistra in följande kod i chat.gateway.ts.

importera {
MessageBody,
Prenumerera meddelande,
WebSocketGateway,
WebSocketServer,
} från'@nestjs/websockets';
importera { Server } från'socket.io';

@WebSocketGateway()
exporteraklassChatGateway{
@WebSocketServer()
server: Server;
// lyssna efter send_message-händelser
@SubscribeMessage('skicka meddelande')
listenForMessages(@MessageBody() meddelande: sträng) {
detta.server.sockets.emit('receive_message', meddelande);
}
}

Autentisera anslutna användare

Autentisering är en viktig del av webbapplikationer, och det är inte annorlunda för en chattapplikation. Funktionen för att autentisera klientanslutningar till socket finns i chats.service.ts som visas här:

@Injicerbar()
exporteraklassChatsService{
konstruktör(privat authService: AuthService) {}

asynkron getUserFromSocket (socket: Socket) {
låta auth_token = socket.handshake.headers.authorization;
// få själva token utan "Bearer"
auth_token = auth_token.split(' ')[1];

konst användare = detta.authService.getUserFromAuthenticationToken(
auth_token
);

om (!användare) {
kastany WsException('Ogiltiga uppgifter.');
}
lämna tillbaka användare;
}
}

De getUserFromSocket metod använder getUserFromAuthenticationToken för att hämta den för närvarande inloggade användaren från JWT-token genom att extrahera bärartoken. De getUserFromAuthenticationToken funktionen är implementerad i auth.service.ts fil som visas här:

offentlig asynkron getUserFromAuthenticationToken (token: sträng) {
konst nyttolast: JwtPayload = detta.jwtService.verify (token, {
hemlighet: detta.configService.get('JWT_ACCESS_TOKEN_SECRET'),
});

konst userId = nyttolast.sub

om (användar ID) {
lämna tillbakadetta.usersService.findById (userId);
}
}

Det aktuella uttaget skickas som en parameter till getUserFromSocket när hanteraAnslutning metod av ChatGateway implementerar OnGatewayConnection gränssnitt. Detta gör det möjligt att ta emot meddelanden och information om den för närvarande anslutna användaren.

Koden nedan visar detta:

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

konstruktör(privat chatsService: ChatsService) {}

asynkron handleConnection (socket: Socket) {
väntadetta.chatsService.getUserFromSocket (socket)
}

@SubscribeMessage('skicka meddelande')
asynkron listenForMessages(@MessageBody() meddelande: sträng, @ConnectedSocket() socket: Socket) {

konst användare = väntadetta.chatsService.getUserFromSocket (socket)
detta.server.sockets.emit('receive_message', {
meddelande,
användare
});
}
}

Du kan referera till filerna som är involverade i autentiseringssystemet ovan i GitHub-förråd för att se de fullständiga koderna (inklusive importer), för en bättre förståelse av implementeringen.

Fortsatta chattar till databas

För att användare ska se sin meddelandehistorik behöver du ett schema för att lagra meddelanden. Skapa en ny fil som heter meddelande.schema.ts och klistra in koden nedan i den (kom ihåg att importera din användarschema eller kolla in förvaret för en).

importera { Användare } från'./../users/schemas/user.schema';
importera { Prop, Schema, SchemaFactory } från"@nestjs/mongoose";
importera mangust, { Dokument } från"mungo";

exportera typ MessageDocument = Meddelande & dokument;

@Schema({
tillJSON: {
getters: Sann,
virtuella: Sann,
},
tidsstämplar: Sann,
})
exporteraklassMeddelande{
@Stötta({ nödvändig: Sann, unik: Sann })
meddelande: sträng

@Stötta({ typ: mangust. Schema. Typer. ObjectId, ref: 'Användare' })
användare: Användare
}

konst MessageSchema = SchemaFactory.createForClass (Meddelande)

exportera { MessageSchema };

Nedan är en implementering av tjänster för att skapa ett nytt meddelande och få in alla meddelanden chats.service.ts.

importera { Meddelande, meddelandedokument } från'./meddelande.schema'; 
importera { Socket } från'socket.io';
importera { AuthService } från'./../auth/auth.service';
importera { Injicerbar } från'@nestjs/common';
importera { WsException } från'@nestjs/websockets';
importera { InjectModel } från'@nestjs/mongoose';
importera { Modell } från'mungo';
importera { MessageDto } från'./dto/meddelande.dto';

@Injicerbar()
exporteraklassChatsService{
konstruktör(privat authService: AuthService, @InjectModel (Message.name) privat meddelandeModel: Model) {}
...
asynkron createMessage (meddelande: MessageDto, användar ID: sträng) {
konst newMessage = nydetta.messageModel({...meddelande, användar-ID})
vänta newMessage.save
lämna tillbaka nytt meddelande
}
asynkron getAllMessages() {
lämna tillbakadetta.messageModel.find().populate('användare')
}
}

De MessageDto genomförs i en meddelande.dto.ts fil i dto mapp i chattar katalog. Du kan också hitta den i arkivet.

Du måste lägga till meddelande modell och schema till listan över importer i chats.modul.ts.

importera { Meddelande, meddelandeschema } från'./meddelande.schema';
importera { Modul } från'@nestjs/common';
importera { ChatGateway } från'./chats.gateway';
importera { ChatsService } från'./chats.service';
importera { MongooseModule } från'@nestjs/mongoose';

@Modul({
importer: [MongooseModule.forFeature([
{ namn: Message.name, schema: MessageSchema }
])],
kontroller: [],
leverantörer: [ChatsService, ChatGateway]
})
exporteraklassChatsModule{}

Slutligen, den få_alla_meddelanden händelsehanteraren läggs till i ChatGateway klass i chat.gateway.ts som visas i följande kod:

// importerar...

@WebSocketGateway()
exporteraklassChatGatewayredskapOnGatewayConnection{
...

@SubscribeMessage("get_alla_meddelanden")
asynkron getAllMessages(@ConnectedSocket() socket: Socket) {

väntadetta.chatsService.getUserFromSocket (socket)
konst meddelanden = väntadetta.chatsService.getAllMessages()

detta.server.sockets.emit('receive_message', meddelanden);

lämna tillbaka meddelanden
}
}

När en ansluten klient (användare) sänder ut få_alla_meddelanden händelse kommer alla deras meddelanden att hämtas och när de sänder ut skicka meddelande, ett meddelande skapas och lagras i databasen och skickas sedan till alla andra anslutna klienter.

När du är klar med alla ovanstående steg kan du starta din applikation med npm körstart: dev, och testa det med en WebSocket-klient som Postman.

Bygga realtidsapplikationer med NestJS

Även om det finns andra tekniker för att bygga realtidssystem är WebSockets väldigt populära och lätta att implementera i många fall, och de är det bästa alternativet för chattapplikationer.

Realtidsapplikationer är inte bara begränsade till chattapplikationer, andra exempel inkluderar videoströmning eller anropsapplikationer och liveväderapplikationer, och NestJS tillhandahåller fantastiska verktyg för att bygga i realtid appar.