WebSockets в TypeScript: Создаем real-time приложения с нуля

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему WebSockets — это магия real-time?

Представьте, что вы открыли чат с поддержкой, отправили сообщение и... ждете. Секунда, две, пять. Знакомо? Это классический HTTP-запрос: вы просите, сервер отвечает. А что если сервер сам хочет что-то сказать? Например, новый заказ в CRM или голос в онлайн-игре? Тут HTTP пасует — ему нужен постоянный пинг-понг.

WebSockets решают эту проблему кардинально. Они создают постоянный двусторонний канал между клиентом и сервером. Один раз «рукопожатие» — и данные летят в обе стороны мгновенно. Никакого лишнего трафика, никаких задержек. Только чистая магия real-time.

В этой статье мы на TypeScript соберем полноценное real-time приложение. Научимся работать с WebSocket сервером на Node.js, настроим клиент и даже добавим обработку ошибок. К концу вы сможете написать свой чат, систему уведомлений или многопользовательскую игру.

Что такое WebSockets и зачем они нужны?

WebSocket — это протокол поверх TCP, который обеспечивает постоянное соединение между браузером и сервером. В отличие от HTTP, где каждый запрос открывает и закрывает соединение, WebSocket держит его открытым.

Где это критично?

  • Чаты и мессенджеры — сообщения приходят без задержки.
  • Онлайн-игры — позиции игроков обновляются в реальном времени.
  • Биржевые котировки — цены меняются каждую миллисекунду.
  • Совместное редактирование — как Google Docs.

TypeScript добавляет к этому строгую типизацию. Вы не отправите случайно строку вместо числа — компилятор поймает ошибку на этапе разработки. Это спасает от багов в продакшене.

Настройка проекта: Node.js + TypeScript + WebSocket

Начнем с инициализации. Убедитесь, что у вас установлены Node.js (версия 18+) и npm.

mkdir websocket-ts-chat
cd websocket-ts-chat
npm init -y
npm install ws @types/ws typescript ts-node @types/node
npx tsc --init

В tsconfig.json включите строгий режим и укажите outDir: "./dist". Это база для любого TypeScript проекта.

Пишем WebSocket сервер на TypeScript

Создайте файл server.ts. Здесь мы поднимем WebSocket сервер на порту 8080.

import { WebSocketServer, WebSocket } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

interface CustomWebSocket extends WebSocket {
    id: string;
    username?: string;
}

wss.on('connection', (ws: CustomWebSocket) => {
    ws.id = generateId();
    console.log(`Клиент подключился: ${ws.id}`);

    ws.on('message', (data: Buffer) => {
        const message = data.toString();
        console.log(`Получено: ${message}`);

        // Рассылаем всем, кроме отправителя
        wss.clients.forEach((client) => {
            if (client !== ws && client.readyState === WebSocket.OPEN) {
                client.send(`[${ws.id}]: ${message}`);
            }
        });
    });

    ws.on('close', () => {
        console.log(`Клиент отключился: ${ws.id}`);
    });

    ws.send('Добро пожаловать в чат!');
});

function generateId(): string {
    return Math.random().toString(36).substring(2, 9);
}

console.log('Сервер запущен на ws://localhost:8080');

Разберем ключевые моменты:

  • WebSocketServer — создает сервер.
  • connection — событие при подключении нового клиента.
  • message — получаем данные от клиента.
  • send — отправляем данные обратно.

Запустите сервер командой npx ts-node server.ts. Он будет слушать порт 8080.

Клиентская часть: подключаемся к WebSocket

Теперь создадим client.ts. Это будет простой скрипт, который подключается к серверу и отправляет сообщения.

import WebSocket from 'ws';

const ws = new WebSocket('ws://localhost:8080');

ws.on('open', () => {
    console.log('Подключено к серверу');
    ws.send('Привет, сервер!');
});

ws.on('message', (data: Buffer) => {
    console.log(`Сервер: ${data.toString()}`);
});

ws.on('close', () => {
    console.log('Соединение закрыто');
});

ws.on('error', (error: Error) => {
    console.error('Ошибка:', error.message);
});

// Отправляем сообщение каждые 5 секунд
setInterval(() => {
    ws.send(`Сообщение в ${new Date().toLocaleTimeString()}`);
}, 5000);

Запустите клиент в другом терминале: npx ts-node client.ts. Вы увидите, как сервер отвечает на каждое сообщение. Это и есть real-time магия.

Добавляем типизацию сообщений

Одно из главных преимуществ TypeScript — строгие типы. Давайте определим формат сообщений.

interface ChatMessage {
    type: 'message' | 'system' | 'error';
    username: string;
    content: string;
    timestamp: number;
}

// Отправка типизированного сообщения
const msg: ChatMessage = {
    type: 'message',
    username: 'Alice',
    content: 'Привет!',
    timestamp: Date.now()
};

ws.send(JSON.stringify(msg));

Теперь, когда клиент или сервер получают данные, они знают точную структуру. Никаких сюрпризов.

Обработка ошибок и переподключение

В real-time приложениях соединение может оборваться. Клиент должен уметь переподключаться.

function connectWithRetry(url: string, retries: number = 5): WebSocket {
    let ws = new WebSocket(url);

    ws.on('close', () => {
        if (retries > 0) {
            console.log(`Попытка переподключения... Осталось ${retries} попыток`);
            setTimeout(() => {
                ws = connectWithRetry(url, retries - 1);
            }, 2000);
        } else {
            console.error('Не удалось восстановить соединение');
        }
    });

    ws.on('error', (err: Error) => {
        console.error('Ошибка соединения:', err.message);
    });

    return ws;
}

const ws = connectWithRetry('ws://localhost:8080');

Этот паттерн используется в продакшене. Он гарантирует, что приложение не упадет при временных сбоях сети.

Расширяем функционал: комнаты и авторизация

Наш чат пока один на всех. Добавим комнаты — пользователи смогут общаться в изолированных группах.

interface RoomMessage {
    room: string;
    sender: string;
    text: string;
}

// Сервер: обработка комнат
const rooms = new Map>();

ws.on('message', (data: Buffer) => {
    const parsed: RoomMessage = JSON.parse(data.toString());
    const { room, sender, text } = parsed;

    if (!rooms.has(                

Блоги

Рекомендаций по книгам