Μάθετε πώς να δημιουργείτε ένα API συνομιλίας σε πραγματικό χρόνο αξιοποιώντας τη δύναμη των WebSockets χρησιμοποιώντας το NestJS.

Το NestJS είναι ένα δημοφιλές πλαίσιο για τη δημιουργία εφαρμογών από την πλευρά του διακομιστή με το Node.js. Με την υποστήριξή του για WebSockets, το NestJS είναι κατάλληλο για την ανάπτυξη εφαρμογών συνομιλίας σε πραγματικό χρόνο.

Λοιπόν, τι είναι τα WebSockets και πώς μπορείτε να δημιουργήσετε μια εφαρμογή συνομιλίας σε πραγματικό χρόνο στο NestJS;

Τι είναι τα WebSockets;

Τα WebSockets είναι ένα πρωτόκολλο για μόνιμη, σε πραγματικό χρόνο και αμφίδρομη επικοινωνία μεταξύ ενός πελάτη και ενός διακομιστή.

Σε αντίθεση με το HTTP όπου μια σύνδεση κλείνει όταν ολοκληρώνεται ένας κύκλος αιτήματος μεταξύ του πελάτη και του διακομιστή, μια σύνδεση WebSocket διατηρείται ανοιχτή και δεν κλείνει ακόμα και μετά την επιστροφή μιας απάντησης για α αίτηση.

Η παρακάτω εικόνα είναι μια απεικόνιση του τρόπου λειτουργίας μιας επικοινωνίας WebSocket μεταξύ διακομιστή και πελάτη:

instagram viewer

Για τη δημιουργία αμφίδρομης επικοινωνίας, ο πελάτης στέλνει ένα αίτημα χειραψίας WebSocket στον διακομιστή. Οι κεφαλίδες αιτημάτων περιέχουν ένα ασφαλές κλειδί WebSocket (Sec-WebSocket-Key), και ένα Αναβάθμιση: WebSocket κεφαλίδα η οποία μαζί με το Σύνδεση: Αναβάθμιση Η κεφαλίδα λέει στον διακομιστή να αναβαθμίσει το πρωτόκολλο από HTTP σε WebSocket και να διατηρήσει τη σύνδεση ανοιχτή. Μαθαίνω για WebSockets σε JavaScript βοηθά στην ακόμα καλύτερη κατανόηση της έννοιας.

Δημιουργία ενός API συνομιλίας σε πραγματικό χρόνο χρησιμοποιώντας τη μονάδα NestJS WebSocket

Το Node.js παρέχει δύο σημαντικές υλοποιήσεις WebSockets. Το πρώτο είναι ws που υλοποιεί γυμνά WebSockets. Και το δεύτερο είναι socket.io, το οποίο παρέχει περισσότερες λειτουργίες υψηλού επιπέδου.

Το NestJS έχει ενότητες και για τα δύο socket.io και ws. Αυτό το άρθρο χρησιμοποιεί το socket.io ενότητα για τις δυνατότητες WebSocket της εφαρμογής δείγματος.

Ο κώδικας που χρησιμοποιείται σε αυτό το έργο είναι διαθέσιμος στο α Αποθετήριο GitHub. Συνιστάται να το κλωνοποιήσετε τοπικά για να κατανοήσετε καλύτερα τη δομή του καταλόγου και να δείτε πώς αλληλεπιδρούν όλοι οι κωδικοί μεταξύ τους.

Ρύθμιση και εγκατάσταση έργου

Ανοίξτε το τερματικό σας και δημιουργήστε μια νέα εφαρμογή NestJS χρησιμοποιώντας το φωλιά καινούργια εντολή (π.χ. nest new chat-app). Η εντολή δημιουργεί έναν νέο κατάλογο που περιέχει τα αρχεία του έργου. Τώρα είστε έτοιμοι να ξεκινήσετε τη διαδικασία ανάπτυξης.

Ρυθμίστε μια σύνδεση MongoDB

Για να παραμείνουν τα μηνύματα συνομιλίας στην εφαρμογή, χρειάζεστε μια βάση δεδομένων. Αυτό το άρθρο χρησιμοποιεί τη βάση δεδομένων MongoDB για την εφαρμογή μας NestJS, και ο ευκολότερος τρόπος για να τρέξετε είναι να ρυθμίστε ένα σύμπλεγμα MongoDB στο σύννεφο και λάβετε τη διεύθυνση URL MongoDB σας. Αντιγράψτε τη διεύθυνση URL και αποθηκεύστε την ως MONGO_URI μεταβλητή σε σας .env αρχείο.

Θα χρειαστείτε επίσης το Mongoose αργότερα όταν κάνετε ερωτήσεις στο MongoDB. Εγκαταστήστε το εκτελώντας npm εγκατάσταση mongoose στο τερματικό σας.

Στο src φάκελο, δημιουργήστε ένα αρχείο που ονομάζεται mongo.config.ts και επικολλήστε τον παρακάτω κώδικα σε αυτό.

εισαγωγή { registerAs } από'@nestjs/config';

/**
* Διαμόρφωση σύνδεσης βάσης δεδομένων Mongo
*/

εξαγωγήΠροκαθορισμένο registerAs('mongodb', () => {
συνθ { MONGO_URI } = process.env; // από το αρχείο .env
ΕΠΙΣΤΡΟΦΗ {
uri:`${MONGO_URI}`,
};
});

Το έργο σας κύρια.τς το αρχείο θα πρέπει να μοιάζει με αυτό:

εισαγωγή { NestFactory } από'@nestjs/core';
εισαγωγή { AppModule } από"./app.module";
εισαγωγή * όπως και cookieParser από"cookie-parser"
εισαγωγή κράνος από'κράνος'
εισαγωγή { Logger, ValidationPipe } από'@nestjs/common';
εισαγωγή { setupSwagger } από'./utils/swagger';
εισαγωγή { HttpExceptionFilter } από'./filters/http-exception.filter';

ασυγχρονισμόςλειτουργίαbootstrap() {
συνθ εφαρμογή = αναμένω NestFactory.create (AppModule, { cors: αληθής });
app.enableCors({
προέλευση: '*',
διαπιστευτήρια: αληθής
})
app.use (cookieParser())
app.useGlobalPipes(
νέος ValidationPipe({
λευκή λίστα: αληθής
})
)
συνθ ξυλοκόπος = νέος Κόπτων δέντρα διά ξυλείαν('Κύριος')

app.setGlobalPrefix('api/v1')
app.useGlobalFilters(νέος HttpExceptionFilter());

setupSwagger (εφαρμογή)
app.use (κράνος())

αναμένω app.listen (AppModule.port)

// ημερολόγιο εγγράφων
συνθ baseUrl = AppModule.getBaseUrl (εφαρμογή)
συνθ url = `http://${baseUrl}:${AppModule.port}`
logger.log(`Τεκμηρίωση API διαθέσιμη στη διεύθυνση ${url}/docs`);
}
bootstrap();

Δημιουργία της ενότητας συνομιλίας

Για να ξεκινήσετε με τη δυνατότητα συνομιλίας σε πραγματικό χρόνο, το πρώτο βήμα είναι να εγκαταστήσετε τα πακέτα NestJS WebSockets. Αυτό μπορεί να γίνει εκτελώντας την ακόλουθη εντολή στο τερματικό.

npm εγκατάσταση @nestjs/websockets @nestjs/platform-socket.io @types/socket.io

Μετά την εγκατάσταση των πακέτων, πρέπει να δημιουργήσετε τη μονάδα συνομιλιών εκτελώντας τις ακόλουθες εντολές

nest g module chats
nest g συνομιλίες ελεγκτή
συνομιλίες υπηρεσίας nest g

Μόλις ολοκληρωθεί η δημιουργία της λειτουργικής μονάδας, το επόμενο βήμα είναι να δημιουργήσετε μια σύνδεση WebSockets στο NestJS. Δημιουργώ ένα chat.gateway.ts αρχείο μέσα στο συνομιλίες φάκελο, εδώ υλοποιείται η πύλη που στέλνει και λαμβάνει μηνύματα.

Επικολλήστε τον παρακάτω κώδικα chat.gateway.ts.

εισαγωγή {
MessageBody,
Μήνυμα Εγγραφής,
WebSocketGateway,
WebSocketServer,
} από'@nestjs/websockets';
εισαγωγή { Διακομιστής } από'socket.io';

@WebSocketGateway()
εξαγωγήτάξηChatGateway{
@WebSocketServer()
διακομιστής: Διακομιστής;
// ακούστε για συμβάντα send_message
@SubscribeMessage('να στείλετε μήνυμα')
listenForMessages(@MessageBody() μήνυμα: συμβολοσειρά) {
Αυτό.server.sockets.emit("receive_message", μήνυμα);
}
}

Έλεγχος ταυτότητας συνδεδεμένων χρηστών

Ο έλεγχος ταυτότητας είναι ένα ουσιαστικό μέρος των εφαρμογών Ιστού και δεν διαφέρει σε μια εφαρμογή συνομιλίας. Η λειτουργία για τον έλεγχο ταυτότητας των συνδέσεων πελάτη στην υποδοχή βρίσκεται στο chats.service.ts όπως φαίνεται εδώ:

@Ενεκτική()
εξαγωγήτάξηChatsService{
κατασκευαστής(private authService: AuthService) {}

ασυγχρονισμός getUserFromSocket (υποδοχή: Socket) {
αφήνω auth_token = socket.handshake.headers.authorization;
// λάβετε το ίδιο το διακριτικό χωρίς "Bearer"
auth_token = auth_token.split(' ')[1];

συνθ χρήστης = Αυτό.authService.getUserFromAuthenticationToken(
auth_token
);

αν (!χρήστης) {
βολήνέος WsException('Ακυρα διαπιστευτήρια.');
}
ΕΠΙΣΤΡΟΦΗ χρήστης;
}
}

ο getUserFromSocket χρήσεις μεθόδων getUserFromAuthenticationToken για να λάβετε τον τρέχοντα συνδεδεμένο χρήστη από το διακριτικό JWT εξάγοντας το διακριτικό Bearer. ο getUserFromAuthenticationToken η λειτουργία υλοποιείται στο auth.service.ts αρχείο όπως φαίνεται εδώ:

δημόσιο ασυγχρονισμός getUserFromAuthenticationToken (token: string) {
συνθ ωφέλιμο φορτίο: JwtPayload = Αυτό.jwtService.verify (token, {
μυστικό: Αυτό.configService.get('JWT_ACCESS_TOKEN_SECRET'),
});

συνθ userId = payload.sub

αν (ταυτότητα χρήστη) {
ΕΠΙΣΤΡΟΦΗΑυτό.usersService.findById (userId);
}
}

Η τρέχουσα υποδοχή μεταβιβάζεται ως παράμετρος στην getUserFromSocket όταν ο λαβήΣύνδεση μέθοδος για ChatGateway υλοποιεί το OnGatewayConnection διεπαφή. Αυτό καθιστά δυνατή τη λήψη μηνυμάτων και πληροφοριών σχετικά με τον τρέχοντα συνδεδεμένο χρήστη.

Ο παρακάτω κώδικας το δείχνει αυτό:

// chat.gateway.ts
@WebSocketGateway()
εξαγωγήτάξηChatGatewayυλοποιείOnGatewayConnection{
@WebSocketServer()
διακομιστής: Διακομιστής;

κατασκευαστής(private chatsService: ChatsService) {}

ασυγχρονισμός handleConnection (πρίζα: Socket) {
αναμένωΑυτό.chatsService.getUserFromSocket (υποδοχή)
}

@SubscribeMessage('να στείλετε μήνυμα')
ασυγχρονισμός listenForMessages(@MessageBody() μήνυμα: string, @ConnectedSocket() socket: Socket) {

συνθ χρήστης = αναμένωΑυτό.chatsService.getUserFromSocket (υποδοχή)
Αυτό.server.sockets.emit("receive_message", {
μήνυμα,
χρήστης
});
}
}

Μπορείτε να αναφέρετε τα αρχεία που εμπλέκονται στο σύστημα ελέγχου ταυτότητας παραπάνω στο Αποθετήριο GitHub για να δείτε τους πλήρεις κωδικούς (συμπεριλαμβανομένων των εισαγωγών), για καλύτερη κατανόηση της υλοποίησης.

Συνεχείς συνομιλίες στη βάση δεδομένων

Για να δουν οι χρήστες το ιστορικό μηνυμάτων τους, χρειάζεστε ένα σχήμα για την αποθήκευση των μηνυμάτων. Δημιουργήστε ένα νέο αρχείο που ονομάζεται message.schema.ts και επικολλήστε τον παρακάτω κώδικα σε αυτό (θυμηθείτε να εισαγάγετε το δικό σας σχήμα χρήστη ή ελέγξτε το αποθετήριο για ένα).

εισαγωγή { Χρήστης } από"./../users/schemas/user.schema";
εισαγωγή { Prop, Schema, SchemaFactory } από"@nestjs/mongoose";
εισαγωγή μαγκούστα, { Document } από"μαγκούστα";

εξαγωγή πληκτρολογήστε MessageDocument = Message & Document;

@Σχήμα({
toJSON: {
λήπτες: αληθής,
εικονικά: αληθής,
},
χρονικές σημάνσεις: αληθής,
})
εξαγωγήτάξηΜήνυμα{
@Στήριγμα({ απαιτείται: αληθής, μοναδικός: αληθής })
μήνυμα: συμβολοσειρά

@Στήριγμα({ τύπος: μαγκούστα. Σχήμα. Τύποι. Αναγνωριστικό αντικειμένου, αναφ: 'Χρήστης' })
χρήστης: Χρήστης
}

συνθ MessageSchema = SchemaFactory.createForClass (Μήνυμα)

εξαγωγή { MessageSchema };

Ακολουθεί μια εφαρμογή υπηρεσιών για τη δημιουργία ενός νέου μηνύματος και τη λήψη όλων των μηνυμάτων chats.service.ts.

εισαγωγή { Message, MessageDocument } από"./message.schema"; 
εισαγωγή { Πρίζα } από'socket.io';
εισαγωγή { AuthService } από"./../auth/auth.service";
εισαγωγή { Ενέσιμο } από'@nestjs/common';
εισαγωγή { WsException } από'@nestjs/websockets';
εισαγωγή { InjectModel } από'@nestjs/mongoose';
εισαγωγή { Μοντέλο } από'μαγκούστα';
εισαγωγή { MessageDto } από"./dto/message.dto";

@Ενεκτική()
εξαγωγήτάξηChatsService{
κατασκευαστής(private authService: AuthService, @InjectModel (Message.name) ιδιωτικό μήνυμαΜοντέλο: Μοντέλο) {}
...
ασυγχρονισμός createMessage (μήνυμα: MessageDto, ταυτότητα χρήστη: συμβολοσειρά) {
συνθ νέοΜήνυμα = νέοςΑυτό.messageModel({...message, userId})
αναμένω newMessage.save
ΕΠΙΣΤΡΟΦΗ νέο μήνυμα
}
ασυγχρονισμός getAllMessages() {
ΕΠΙΣΤΡΟΦΗΑυτό.messageModel.find().populate('χρήστης')
}
}

ο ΜήνυμαDto υλοποιείται σε α μήνυμα.dto.ts αρχείο στο dto φάκελο στο συνομιλίες Ευρετήριο. Μπορείτε επίσης να το βρείτε στο αποθετήριο.

Πρέπει να προσθέσετε το μήνυμα μοντέλο και σχήμα στη λίστα εισαγωγών chats.module.ts.

εισαγωγή { Message, MessageSchema } από"./message.schema";
εισαγωγή { Ενότητα } από'@nestjs/common';
εισαγωγή { ChatGateway } από"./chats.gateway";
εισαγωγή { ChatsService } από"./chats.service";
εισαγωγή { MongooseModule } από'@nestjs/mongoose';

@Μονάδα μέτρησης({
εισαγωγές: [MongooseModule.forFeature([
{ όνομα: Message.name, σχήμα: MessageSchema }
])],
ελεγκτές: [],
πάροχοι: [ChatsService, ChatGateway]
})
εξαγωγήτάξηChatsModule{}

Τέλος, το get_all_messages Ο χειριστής συμβάντων προστίθεται στο ChatGateway τάξη μέσα chat.gateway.ts όπως φαίνεται στον παρακάτω κώδικα:

// εισαγωγές...

@WebSocketGateway()
εξαγωγήτάξηChatGatewayυλοποιείOnGatewayConnection{
...

@SubscribeMessage("get_all_messages")
ασυγχρονισμός υποδοχή getAllMessages(@ConnectedSocket(): Socket) {

αναμένωΑυτό.chatsService.getUserFromSocket (υποδοχή)
συνθ μηνύματα = αναμένωΑυτό.chatsService.getAllMessages()

Αυτό.server.sockets.emit("receive_message", μηνύματα);

ΕΠΙΣΤΡΟΦΗ μηνύματα
}
}

Όταν ένας συνδεδεμένος πελάτης (χρήστης) εκπέμπει το get_all_messages εκδήλωση, όλα τα μηνύματά τους θα ανακτηθούν και πότε θα εκπέμψουν να στείλετε μήνυμα, ένα μήνυμα δημιουργείται και αποθηκεύεται στη βάση δεδομένων και στη συνέχεια αποστέλλεται σε όλους τους άλλους συνδεδεμένους πελάτες.

Αφού ολοκληρώσετε όλα τα παραπάνω βήματα, μπορείτε να ξεκινήσετε τη χρήση της εφαρμογής σας npm έναρξη εκτέλεσης: devκαι δοκιμάστε το με έναν πελάτη WebSocket όπως ο Postman.

Δημιουργία εφαρμογών σε πραγματικό χρόνο με το NestJS

Αν και υπάρχουν άλλες τεχνολογίες για την κατασκευή συστημάτων σε πραγματικό χρόνο, τα WebSockets είναι πολύ δημοφιλή και εύκολα στην εφαρμογή τους σε πολλές περιπτώσεις και αποτελούν την καλύτερη επιλογή για εφαρμογές συνομιλίας.

Οι εφαρμογές σε πραγματικό χρόνο δεν περιορίζονται μόνο σε εφαρμογές συνομιλίας, άλλα παραδείγματα περιλαμβάνουν ροή βίντεο ή εφαρμογές κλήσης και ζωντανές εφαρμογές καιρού και το NestJS παρέχει εξαιρετικά εργαλεία για τη δημιουργία σε πραγματικό χρόνο εφαρμογές.