Узнайте, как создать API чата в реальном времени, используя возможности WebSockets с помощью NestJS.

NestJS — это популярная платформа для создания серверных приложений с помощью Node.js. Благодаря поддержке WebSockets NestJS хорошо подходит для разработки приложений для чата в реальном времени.

Итак, что такое WebSockets и как создать приложение для чата в реальном времени в NestJS?

Что такое веб-сокеты?

WebSockets — это протокол для постоянной двусторонней связи в режиме реального времени между клиентом и сервером.

В отличие от HTTP, где соединение закрывается после завершения цикла запроса между клиентом и сервером, соединение WebSocket остается открытым и не закрывается даже после того, как ответ был возвращен для запрос.

Изображение ниже представляет собой визуализацию того, как работает связь WebSocket между сервером и клиентом:

Чтобы установить двустороннюю связь, клиент отправляет на сервер запрос на установление связи WebSocket. Заголовки запроса содержат безопасный ключ WebSocket (

instagram viewer
Sec-WebSocket-ключ), и Обновление: веб-сокет заголовок, который вместе с Подключение: Обновление Заголовок указывает серверу обновить протокол с HTTP до WebSocket и оставить соединение открытым. Узнавать о Веб-сокеты в JavaScript помогает лучше понять концепцию.

Создание API чата в реальном времени с использованием модуля NestJS WebSocket

Node.js предоставляет две основные реализации WebSockets. Первый ws который реализует голые веб-сокеты. И второй socket.io, который предоставляет более высокоуровневые функции.

NestJS имеет модули для обоих socket.io и ws. В этой статье используется socket.io модуль для функций WebSocket примера приложения.

Код, используемый в этом проекте, доступен в Репозиторий GitHub. Рекомендуется клонировать его локально, чтобы лучше понять структуру каталогов и увидеть, как все коды взаимодействуют друг с другом.

Настройка и установка проекта

Откройте свой терминал и создайте новое приложение NestJS, используя гнездо новое команда (напр. гнездо нового чат-приложения). Команда создает новый каталог, содержащий файлы проекта. Теперь вы готовы начать процесс разработки.

Настройка подключения к MongoDB

Чтобы сохранить сообщения чата в приложении, вам нужна база данных. В этой статье используется база данных MongoDB для нашего приложения NestJS, и самый простой способ начать работу — это настроить кластер MongoDB в облаке и получите URL-адрес MongoDB. Скопируйте URL-адрес и сохраните его как MONGO_URI переменная в вашем .env файл.

Вам также понадобится Mongoose позже, когда вы будете делать запросы к MongoDB. Установите его, запустив npm установить мангуста в вашем терминале.

в источник папку, создайте файл с именем монго.config.ts и вставьте в него следующий код.

Импортировать { зарегистрироваться как } от'@nestjs/config';

/**
* Конфигурация подключения к базе данных Mongo
*/

экспортпо умолчанию зарегистрироваться как('mongodb', () => {
константа { MONGO_URI } = процесс.env; // из файла .env
возвращаться {
ури:`${МОНГО_URI}`,
};
});

Ваш проект main.ts файл должен выглядеть так:

Импортировать {Фабрика гнезд} от'@nestjs/ядро';
Импортировать { AppModule } от'./app.module';
Импортировать * как cookieParser от'cookie-парсер'
Импортировать шлем от'шлем'
Импортировать { Регистратор, ValidationPipe } от'@nestjs/общий';
Импортировать { setupSwagger } от'./утилиты/чванство';
Импортировать { Фильтр исключений Http } от'./фильтры/http-исключение.фильтр';

асинхронныйфункцияначальная загрузка() {
константа приложение = Ждите NestFactory.create(AppModule, { кор: истинный });
приложение.enableCors({
источник: '*',
реквизиты для входа: истинный
})
app.use (cookieParser())
приложение.useGlobalPipes(
новый ValidationPipe({
белый список: истинный
})
)
константа регистратор = новый Регистратор('Основной')

app.setGlobalPrefix('апи/v1')
app.useGlobalFilters(новый HttpExceptionFilter());

setupSwagger (приложение)
app.use (шлем())

Ждите app.listen(AppModule.port)

// регистрируем документы
константа baseUrl = AppModule.getBaseUrl (приложение)
константа URL = `http://${базовый URL}:${AppModule.port}`
logger.log(`Документация по API доступна по адресу ${URL}/docs`);
}
начальная загрузка();

Создание модуля чата

Чтобы начать работу с функцией чата в реальном времени, первым шагом будет установка пакетов NestJS WebSockets. Это можно сделать, выполнив следующую команду в терминале.

установка npm @nestjs/websockets @nestjs/platform-socket.io @types/socket.io

После установки пакетов вам необходимо сгенерировать модуль чатов, выполнив следующие команды

чаты модуля Nest g
чаты контроллера Nest G
сервисные чаты Nest G

После создания модуля следующим шагом будет создание соединения WebSockets в NestJS. Создать чат.gateway.ts файл внутри чаты папка, именно здесь реализован шлюз, который отправляет и получает сообщения.

Вставьте следующий код в чат.gateway.ts.

Импортировать {
тело сообщения,
ПодписатьсяСообщение,
WebSocketGateway,
веб-сокетсервер,
} от'@nestjs/вебсокеты';
Импортировать { сервер } от'сокет.ио';

@WebSocketGateway()
экспортсортЧатШлюз{
@Веб-сокетсервер()
сервер: Сервер;
// прослушивание событий send_message
@ПодписатьСообщение('Отправить сообщение')
listenForMessages(@MessageBody() сообщение: строка) {
этот.server.sockets.emit('получить_сообщение', сообщение);
}
}

Аутентификация подключенных пользователей

Аутентификация — неотъемлемая часть веб-приложений, и она ничем не отличается от приложения чата. Функция аутентификации клиентских подключений к сокету находится в чаты.service.ts как показано здесь:

@Инъекционный()
экспортсортЧатыСервис{
конструктор(частный authService: AuthService) {}

асинхронный getUserFromSocket (сокет: сокет) {
позволять auth_token = socket.handshake.headers.authorization;
// получаем сам токен без "Bearer"
auth_token = auth_token.split(' ')[1];

константа пользователь = этот.authService.getUserFromAuthenticationToken(
auth_token
);

если (!пользователь) {
бросатьновый Исключение(«Недействительные учетные данные».);
}
возвращаться пользователь;
}
}

жетусерфромсокет метод использует GetUserFromAuthenticationToken чтобы получить текущего пользователя, вошедшего в систему, из токена JWT, извлекая токен Bearer. GetUserFromAuthenticationToken функция реализована в auth.service.ts файл, как показано здесь:

публичный асинхронный getUserFromAuthenticationToken (токен: строка) {
константа полезная нагрузка: JwtPayload = этот.jwtService.verify (токен, {
секрет: этот.configService.получить('JWT_ACCESS_TOKEN_SECRET'),
});

константа идентификатор пользователя = полезная нагрузка.sub

если (ID пользователя) {
возвращатьсяэтот.usersService.findById (идентификатор пользователя);
}
}

Текущий сокет передается в качестве параметра жетусерфромсокет когда дескриптор соединения метод ЧатШлюз реализует OnGatewayConnection интерфейс. Это позволяет получать сообщения и информацию о подключенном в данный момент пользователе.

Код ниже демонстрирует это:

// чат.шлюз.ts
@WebSocketGateway()
экспортсортЧатШлюзреализуетOnGatewayConnection{
@Веб-сокетсервер()
сервер: Сервер;

конструктор(приватные чатыService: ChatsService) {}

асинхронный handleConnection (сокет: сокет) {
Ждитеэтот.chatsService.getUserFromSocket (сокет)
}

@ПодписатьСообщение('Отправить сообщение')
асинхронный listenForMessages(@MessageBody() сообщение: строка, @ConnectedSocket() сокет: Socket) {

константа пользователь = Ждитеэтот.chatsService.getUserFromSocket (сокет)
этот.server.sockets.emit('получить_сообщение', {
сообщение,
пользователь
});
}
}

Вы можете сослаться на файлы, задействованные в системе аутентификации выше, в Репозиторий GitHub чтобы увидеть полные коды (включая импорт), для лучшего понимания реализации.

Сохранение чатов в базе данных

Чтобы пользователи могли видеть свою историю сообщений, вам нужна схема для хранения сообщений. Создайте новый файл с именем message.schema.ts и вставьте в него приведенный ниже код (не забудьте импортировать схема пользователя или проверьте репозиторий для одного).

Импортировать { Пользователь } от'./../пользователи/схемы/пользователь.схема';
Импортировать { Опора, схема, фабрика схем} от"@nestjs/мангуст";
Импортировать мангуст, {Документ} от"мангуста";

экспорт введите MessageDocument = Сообщение и Документ;

@Схема({
вJSON: {
геттеры: истинный,
виртуалы: истинный,
},
временные метки: истинный,
})
экспортсортСообщение{
@Prop({ необходимый: истинный, уникальный: истинный })
сообщение: строка

@Prop({ тип: мангуст. Схема. Типы. идентификатор объекта, ссылка: 'Пользователь' })
пользователь: пользователь
}

константа MessageSchema = SchemaFactory.createForClass (Сообщение)

экспорт {Схема сообщения};

Ниже приведена реализация сервисов для создания нового сообщения и получения всех сообщений в чаты.service.ts.

Импортировать { Сообщение, Документ-Сообщение } от'./сообщение.схема'; 
Импортировать { Разъем } от'сокет.ио';
Импортировать { Служба авторизации } от'./../авторизация/авторизация.сервис';
Импортировать { Инъекционный } от'@nestjs/общий';
Импортировать {ВсИсключение} от'@nestjs/вебсокеты';
Импортировать {ИнжектМодель} от'@nestjs/мангуст';
Импортировать {Модель} от'мангуста';
Импортировать { СообщениеDto } от'./dto/сообщение.dto';

@Инъекционный()
экспортсортЧатыСервис{
конструктор(private authService: AuthService, @InjectModel (Message.name) private messageModel: Model) {}
...
асинхронный createMessage (сообщение: MessageDto, ID пользователя: нить) {
константа новое сообщение = новыйэтот.messageModel({...сообщение, идентификатор пользователя})
Ждите новое сообщение.save
возвращаться новое сообщение
}
асинхронный получитьвсе сообщения () {
возвращатьсяэтот.messageModel.find().populate('пользователь')
}
}

СообщениеDto реализуется в сообщение.dto.ts файл в дто папка в чаты каталог. Вы также можете найти его в репозитории.

Вам необходимо добавить сообщение модель и схему в список импорта в чаты.модуль.тс.

Импортировать { Сообщение, схема сообщения } от'./сообщение.схема';
Импортировать {Модуль} от'@nestjs/общий';
Импортировать {Шлюз чата} от'./chats.шлюз';
Импортировать { Служба чатов } от'./chats.service';
Импортировать {Модуль мангуста} от'@nestjs/мангуст';

@Модуль({
импортирует: [MongooseModule.forFeature([
{ имя: Имя.сообщения, схема: Схема сообщения }
])],
контроллеры: [],
провайдеры: [ChatsService, ChatGateway]
})
экспортсортЧатыМодуль{}

Наконец, get_all_messages обработчик событий добавлен в ЧатШлюз класс в чат.gateway.ts как показано в следующем коде:

// импорт...

@WebSocketGateway()
экспортсортЧатШлюзреализуетOnGatewayConnection{
...

@ПодписатьСообщение('get_all_messages')
асинхронный getAllMessages(@ConnectedSocket() сокет: сокет) {

Ждитеэтот.chatsService.getUserFromSocket (сокет)
константа сообщения = Ждитеэтот.chatsService.getAllMessages()

этот.server.sockets.emit('получить_сообщение', Сообщения);

возвращаться Сообщения
}
}

Когда подключенный клиент (пользователь) выдает get_all_messages событие, все их сообщения будут извлечены, и когда они испустят Отправить сообщение, сообщение создается и сохраняется в базе данных, а затем отправляется всем другим подключенным клиентам.

Выполнив все вышеперечисленные шаги, вы можете запустить свое приложение, используя запуск запуска npm: devи протестируйте его с помощью клиента WebSocket, такого как Postman.

Создание приложений реального времени с помощью NestJS

Хотя существуют и другие технологии для создания систем реального времени, веб-сокеты очень популярны и во многих случаях просты в реализации, и они являются лучшим вариантом для чат-приложений.

Приложения реального времени не ограничиваются только чат-приложениями, другие примеры включают потоковое видео или приложения для вызовов и приложения для работы с погодой в реальном времени, а NestJS предоставляет отличные инструменты для создания Программы.