Projetos

Como criar um chat em tempo real na Vercel com WebSockets?

Monte um chat de demonstração com Next.js, Node.js e WebSockets, teste a conexão localmente e entenda quando Redis é necessário para compartilhar mensagens entre instâncias.

Por Equipe Central Stack21/10/202610 minGuia prático
WebSocketsVercelNext.jsNode.jschatprojeto prático

Compartilhar

Envie este conteúdo para sua rede ou salve o link para publicar depois.

Desenvolvedor trabalhando em um notebook em uma mesa de escritório.
Foto: MASUD GAANWALA / Pexels

O que vamos construir e para quem serve

Neste projeto, vamos montar um chat simples de demonstração com Next.js e WebSockets e publicá-lo na Vercel. O navegador mantém uma conexão aberta com o servidor, envia uma mensagem sem criar uma nova requisição HTTP para cada ação e recebe as mensagens em tempo real.

O projeto é para quem já conhece o básico de JavaScript e quer entender comunicação bidirecional na prática. A Vercel anunciou a disponibilidade geral de WebSockets nas Functions em 20 de outubro de 2026. Isso permite hospedar o endpoint junto da aplicação, mas não elimina limites de duração nem cria um banco de dados compartilhado automaticamente.

O exemplo abaixo mantém os sockets em memória e serve para aprender e validar o fluxo. Em produção, instâncias diferentes precisam compartilhar mensagens e estado por um serviço como Redis. A documentação oficial da Vercel usa esse padrão no tutorial completo de chat.

No final, você terá um chat funcional para testar conexões; também vai saber exatamente o que falta antes de torná-lo um serviço público.

Como a conexão funciona

Uma conexão WebSocket começa como uma requisição HTTP que pede uma troca de protocolo. Quando o servidor aceita a troca, cliente e servidor podem enviar mensagens pela mesma conexão. Isso é útil para chats, presença, cursores colaborativos e jogos simples. Para uma resposta de IA que só flui do servidor para o navegador, Server-Sent Events pode ser mais simples; WebSocket faz sentido quando os dois lados enviam mensagens continuamente.

Na Vercel, a conexão fica ligada à Function que a aceitou durante sua vida. Uma conexão nova pode cair em outra instância. Por isso, um conjunto de sockets mantido em uma variável local não é um mecanismo de publicação e assinatura global.

Passo 1: crie o aplicativo Next.js

Instale Node.js e pnpm ou use npm. No terminal, gere um projeto novo. Neste exemplo, escolhemos JavaScript para deixar os arquivos iniciais mais fáceis de acompanhar:

Terminal
pnpm create next-app@latest chat-websocket-vercel --js --app
cd chat-websocket-vercel
pnpm add @vercel/functions

Se você usa npm, troque pnpm create por npx create-next-app@latest e pnpm add por npm install. Aceite a opção App Router se o assistente perguntar. A dependência @vercel/functions fornece a função de upgrade de conexão usada pelo exemplo de Next.js da Vercel.

Passo 2: crie o endpoint WebSocket

Dentro de app, crie as pastas api/ws e o arquivo route.js. O endpoint aceita a conexão, registra o socket em um conjunto local e retransmite cada mensagem para os clientes que estão na mesma instância:

app/api/ws/route.js
import { experimental_upgradeWebSocket } from '@vercel/functions';

const clients = new Set();

export function GET() {
  return experimental_upgradeWebSocket((socket) => {
    clients.add(socket);

    socket.on('message', (data) => {
      const text = data.toString().trim();
      if (!text || text.length > 500) return;

      const message = JSON.stringify({ text, sentAt: new Date().toISOString() });
      for (const client of clients) {
        if (client.readyState === 1) client.send(message);
      }
    });

    const remove = () => clients.delete(socket);
    socket.on('close', remove);
    socket.on('error', remove);
  });
}

O limite de 500 caracteres é apenas uma proteção simples para o exercício, não substitui controle de frequência nem validação de identidade. O conjunto clients existe na memória do processo. Ele desaparece quando a instância é reciclada e não pode entregar a mensagem a um socket conectado em outra instância.

Passo 3: conecte a interface do navegador

Substitua o conteúdo de app/page.js por uma tela básica. Ela abre a conexão ao carregar, mostra o status e envia o texto digitado quando a pessoa aperta o botão:

app/page.js
'use client';

import { useEffect, useRef, useState } from 'react';

export default function Home() {
  const socketRef = useRef(null);
  const [status, setStatus] = useState('conectando');
  const [draft, setDraft] = useState('');
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const scheme = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const socket = new WebSocket(scheme + '//' + window.location.host + '/api/ws');
    socketRef.current = socket;
    socket.onopen = () => setStatus('conectado');
    socket.onclose = () => setStatus('desconectado');
    socket.onerror = () => setStatus('erro na conexão');
    socket.onmessage = (event) => {
      const message = JSON.parse(event.data);
      setMessages((current) => [...current, message]);
    };
    return () => socket.close();
  }, []);

  function sendMessage(event) {
    event.preventDefault();
    const socket = socketRef.current;
    if (!draft.trim() || !socket || socket.readyState !== WebSocket.OPEN) return;
    socket.send(draft.trim());
    setDraft('');
  }

  return (
    <main>
      <h1>Chat de demonstração</h1>
      <p>Conexão: {status}</p>
      <form onSubmit={sendMessage}>
        <input value={draft} onChange={(event) => setDraft(event.target.value)} maxLength={500} />
        <button type='submit'>Enviar</button>
      </form>
      <ul>{messages.map((item, index) => <li key={index}>{item.text}</li>)}</ul>
    </main>
  );
}

Como o servidor envia a mensagem de volta para todos os sockets que consegue enxergar, a própria mensagem aparece na lista. Para estudar primeiro, mantenha a tela simples; depois você pode estilizar o chat, mostrar o horário enviado e exibir mensagens diferentes enquanto a conexão está indisponível.

Passo 4: rode com o runtime local da Vercel

A API experimental de upgrade precisa do runtime da Vercel. Crie ou conecte sua conta e projeto com a CLI, depois inicie o ambiente local:

Terminal
pnpm install
vercel link
vercel dev

Abra o endereço local informado no terminal. O uso de next dev não ativa o manipulador de upgrade da Vercel; para esse caminho de Next.js, teste com vercel dev. Abra duas abas e envie mensagens. Se as duas conexões forem atendidas pela mesma instância local, você verá a retransmissão. Depois teste uma aba em janela anônima para conferir um segundo cliente.

Se a conexão falhar, confirme que a rota é app/api/ws/route.js, que a dependência foi instalada e que o servidor foi iniciado com vercel dev. No navegador, abra as ferramentas de desenvolvedor e confira a aba Network, filtro WS. Um status de upgrade bem-sucedido indica que a conexão foi aberta; um 404 costuma apontar para o caminho ou arquivo incorreto.

Passo 5: publique um teste na Vercel

Envie o repositório para o GitHub e importe-o no painel da Vercel, ou use a CLI depois de conectar o projeto. Faça um deploy de preview antes de compartilhar o endereço. Teste com duas pessoas, dois navegadores e redes diferentes. Essa verificação ajuda a encontrar conexões que chegam a instâncias separadas.

WebSockets exigem Fluid Compute. Ele é habilitado por padrão em projetos mais recentes; projetos antigos podem precisar ativá-lo em Settings, Functions e fazer um novo deploy. A conexão também termina quando a Function alcança a duração máxima do plano, então um cliente real precisa reconectar e recuperar o estado perdido.

O que falta para transformar a demonstração em chat de produção?

O conjunto em memória não é compartilhado entre instâncias. Para que qualquer pessoa veja a mensagem de qualquer outra, adicione uma camada compartilhada de publicação e assinatura, como Redis. O Redis recebe o evento enviado por uma instância e o distribui às outras. A Vercel descreve esse padrão no tutorial oficial de chat, que também trata de salas, histórico, presença, reconexão e variáveis de ambiente.

Um chat público também precisa de autenticação, limites de envio, validação de tamanho e conteúdo, política contra abuso, proteção contra spam e observabilidade. Não coloque mensagens privadas em logs e não assuma que uma conexão aberta identifica uma pessoa. Para salas privadas, valide a autorização antes de aceitar ou retransmitir mensagens.

A Vercel cobra WebSockets segundo o modelo das Functions. O Active CPU é contado enquanto o código processa mensagens; a memória provisionada continua associada à instância enquanto houver trabalho em andamento. Uma conexão aberta não deve ser descrita como totalmente gratuita. Consulte os limites e o uso do seu plano antes de colocar tráfego real.

O próximo passo para um chat confiável é tirar o estado da memória local e compartilhá-lo com Redis, além de adicionar autenticação e reconexão.

Fontes utilizadas