Uma aplicação de chat em tempo real full-stack construída com React Native (bare, sem Expo), Node.js/Express, MongoDB, Socket.IO e TypeScript. O frontend segue os princípios de Clean Architecture organizado por contextos delimitados (auth, users, chat) e usa Zustand para gerenciamento de estado.
- Demonstração
- Visão Geral da Arquitetura
- Stack Tecnológica
- Estrutura do Projeto
- Pré-requisitos
- Instalação e Configuração
- Executando a Aplicação
- Documentação da API
- Eventos WebSocket
- Funcionalidades
- Testes
- Solução de Problemas
demo.webm
O frontend segue Clean Architecture organizada por contextos delimitados:
frontend/src/
├── auth/ # Contexto delimitado de autenticação
│ ├── domain/ # Entidades, objetos de valor, interfaces de repositório
│ ├── application/ # Casos de uso (lógica de negócio)
│ ├── infrastructure/# HTTP, mappers, implementações de repositório
│ └── presentation/ # Telas, view models (Zustand), componentes
├── users/ # Contexto delimitado de usuários
├── chat/ # Contexto delimitado de chat
├── core/ # Componentes UI compartilhados, navegação, tema
└── shared/ # Preocupações transversais (HTTP, storage, DI)
Princípios Fundamentais:
- Camada de domínio: TypeScript puro, sem dependências de frameworks
- Camada de aplicação: Casos de uso orquestram regras de negócio
- Camada de infraestrutura: DTOs, clientes API, WebSocket, implementações de repositório
- Camada de apresentação: Componentes React, stores Zustand, view models
backend/src/
├── config/ # Ambiente, banco de dados, passport, logger
├── modules/
│ ├── auth/ # Autenticação (JWT, Passport Local)
│ ├── users/ # Gerenciamento de usuários
│ └── chat/ # Mensagens e handlers Socket.IO
├── middlewares/ # Tratamento de erros, autenticação
└── utils/ # Utilitários de hash, JWT
- Node.js + Express + TypeScript
- MongoDB (via Mongoose)
- Socket.IO (mensagens em tempo real)
- Passport.js (estratégia Local) + JWT
- bcrypt, Zod, Winston, Helmet, CORS
- Docker e Docker Compose
- React Native 0.82 (bare, sem Expo)
- TypeScript
- Zustand (gerenciamento de estado)
- React Navigation (native stack)
- Axios (HTTP)
- Socket.IO Client (WebSocket)
- AsyncStorage (armazenamento de token)
react-native-realtime-chat/
├── docker-compose.yml
├── .env.example
├── README.md
├── scripts/
│ ├── start-backend-docker.sh
│ ├── seed.sh
│ └── dev-setup.sh
├── backend/
│ ├── Dockerfile
│ ├── package.json
│ ├── tsconfig.json
│ ├── .env.example
│ ├── src/
│ │ ├── server.ts
│ │ ├── app.ts
│ │ ├── config/
│ │ ├── modules/ (auth, users, chat)
│ │ ├── middlewares/
│ │ └── utils/
│ └── tests/
└── frontend/
├── android/
├── ios/
├── src/
│ ├── auth/
│ ├── users/
│ ├── chat/
│ ├── core/
│ ├── shared/
│ └── app/
└── __tests__/
- Node.js >= 20
- Docker e Docker Compose
- iOS: macOS com Xcode, CocoaPods
- Android: Android Studio, JDK 17
git clone <repository-url>
cd react-native-realtime-chatchmod +x scripts/*.sh
./scripts/dev-setup.shIsso irá:
- Instalar dependências do backend
- Instalar dependências do frontend
- Instalar pods do iOS (apenas macOS)
cp .env.example .env
cp backend/.env.example backend/.envEdite backend/.env se necessário (os padrões funcionam para desenvolvimento local).
# Iniciar MongoDB + Backend
./scripts/start-backend-docker.sh
# Ou manualmente:
docker compose up --buildO backend rodará em http://localhost:3001
./scripts/seed.shCria usuários de teste:
alice/password123bob/password123charlie/password123
cd frontend
npm run iosConfiguração de Rede: Usa http://localhost:3001
cd frontend
npm run androidConfiguração de Rede: Usa http://10.0.2.2:3001 (emulador Android)
Alternativa (se usando dispositivo físico):
adb reverse tcp:3001 tcp:3001Depois atualize frontend/src/shared/config/env.ts para usar localhost:3001.
http://localhost:3001/api
Registrar
POST /api/auth/register
Content-Type: application/json
{
"name": "João Silva",
"username": "joaosilva",
"password": "password123"
}
Resposta: 201 Created
{
"status": "success",
"message": "Usuário registrado com sucesso"
}Login
POST /api/auth/login
Content-Type: application/json
{
"username": "alice",
"password": "password123"
}
Resposta: 200 OK
{
"status": "success",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "507f1f77bcf86cd799439011",
"name": "Alice Smith",
"username": "alice"
}
}
}Atualizar Token
POST /api/auth/refresh
Content-Type: application/json
{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Resposta: 200 OK
{
"status": "success",
"data": {
"accessToken": "...",
"refreshToken": "..."
}
}Obter Usuário Atual
GET /api/me
Authorization: Bearer <accessToken>
Resposta: 200 OK
{
"status": "success",
"data": {
"id": "507f1f77bcf86cd799439011",
"name": "Alice Smith",
"username": "alice"
}
}Listar Usuários
GET /api/users
Authorization: Bearer <accessToken>
Resposta: 200 OK
{
"status": "success",
"data": [
{
"id": "507f1f77bcf86cd799439012",
"name": "Bob Johnson",
"username": "bob",
"online": true
},
{
"id": "507f1f77bcf86cd799439013",
"name": "Charlie Brown",
"username": "charlie",
"online": false
}
]
}Obter Mensagens
GET /api/chat/:userId/messages?limit=50&before=2024-01-01T00:00:00.000Z
Authorization: Bearer <accessToken>
Resposta: 200 OK
{
"status": "success",
"data": [
{
"id": "507f1f77bcf86cd799439014",
"from": "507f1f77bcf86cd799439011",
"to": "507f1f77bcf86cd799439012",
"body": "Olá Bob!",
"delivered": true,
"deliveredAt": "2024-01-01T12:00:01.000Z",
"createdAt": "2024-01-01T12:00:00.000Z"
}
]
}Obter Contagens de Mensagens Não Lidas
GET /api/chat/unread-counts
Authorization: Bearer <accessToken>
Resposta: 200 OK
{
"status": "success",
"data": [
{
"conversationWith": "507f1f77bcf86cd799439012",
"count": 3,
"lastMessageAt": "2024-01-01T12:00:00.000Z"
}
]
}Obter Total de Mensagens Não Lidas
GET /api/chat/unread-counts/total
Authorization: Bearer <accessToken>
Resposta: 200 OK
{
"status": "success",
"data": {
"totalCount": 5
}
}Marcar Mensagens como Lidas
PUT /api/chat/:userId/mark-as-read
Authorization: Bearer <accessToken>
Resposta: 200 OK
{
"status": "success",
"message": "Mensagens marcadas como lidas"
}// Conectar com token JWT
socket = io("http://localhost:3001", {
auth: { token: accessToken },
transports: ["websocket"],
});Enviar Mensagem
socket.emit("message:send", {
toUserId: "507f1f77bcf86cd799439012",
body: "Olá!",
});Indicadores de Digitação
socket.emit("typing:start", { toUserId: "..." });
socket.emit("typing:stop", { toUserId: "..." });Marcar como Lida
socket.emit("messages:markAsRead", {
conversationWith: "507f1f77bcf86cd799439012",
});Nova Mensagem
socket.on("message:new", (data) => {
console.log(data.message);
// { id, from, to, body, delivered, createdAt, ... }
});Mensagem Entregue
socket.on("message:delivered", (data) => {
console.log(data);
// { messageId, timestamp }
});Usuário Online/Offline
socket.on("user:online", (data) => {
console.log(`${data.userId} está agora online`);
});
socket.on("user:offline", (data) => {
console.log(`${data.userId} está agora offline`);
});Eventos de Digitação
socket.on("typing:start", (data) => {
console.log(`${data.userId} está digitando...`);
});
socket.on("typing:stop", (data) => {
console.log(`${data.userId} parou de digitar`);
});Contagens de Mensagens Não Lidas Atualizadas
socket.on("unreadCounts:updated", (data) => {
console.log("Contagens atualizadas:", data.unreadCounts);
// Array de { conversationWith: string, count: number }
});- Registro e login de usuários
- Autenticação JWT com refresh token
- Armazenamento seguro de tokens
- Envio e recebimento de mensagens instantâneas
- Indicadores de entrega de mensagem
- Indicadores de digitação em tempo real
- Status de usuário online/offline
- Badges visuais: Contadores de mensagens não lidas na lista de usuários
- Persistência: Contagens armazenadas no MongoDB para manter estado entre sessões
- Tempo real: Atualizações instantâneas via WebSocket quando novas mensagens chegam
- Interface intuitiva: Badges circulares com destaque visual para conversas com mensagens não lidas
- Gerenciamento automático: Contadores zerados automaticamente ao visualizar a conversa
- Design limpo e responsivo
- Lista de usuários com status online
- Tela de chat com histórico de mensagens
- Componentes reutilizáveis seguindo Clean Architecture
- Clean Architecture com contextos delimitados
- Injeção de dependência
- Gerenciamento de estado com Zustand
- Código TypeScript tipado
cd backend
npm testInclui:
- Testes E2E de autenticação (registro, login)
- Testes unitários (utilitários de hash)
cd frontend
npm testInclui:
- Teste unitário de caso de uso (
LoginUseCase)
Problema: Porta 3001 já está em uso
# Encontrar e matar processo
lsof -ti:3001 | xargs kill -9Problema: Erro de conexão com MongoDB
# Reiniciar containers Docker
docker compose down
docker compose up --buildiOS Simulator
- Certifique-se de que o backend roda em
http://localhost:3001 - Verifique
frontend/src/shared/config/env.ts
Emulador Android
- Usa
http://10.0.2.2:3001por padrão - OU use
adb reverse:adb reverse tcp:3001 tcp:3001
Dispositivo Físico
- Substitua
localhostpelo IP local da sua máquina (ex:http://192.168.1.100:3001) - Certifique-se de que o dispositivo está na mesma rede
- Verifique se o token JWT é válido
- Verifique se o servidor Socket.IO está rodando (
docker compose logs backend) - Verifique configurações de firewall/rede
- Certifique-se de que os transports incluem
websocket:io(url, { transports: ["websocket"] });
iOS
cd frontend/ios
pod deintegrate
pod install
cd ..
npm run iosAndroid
cd frontend/android
./gradlew clean
cd ..
npm run android- Tokens JWT expiram em 30 dias (configurável em
backend/.env) - Refresh tokens expiram em 90 dias
- Senhas hasheadas com bcrypt (10 salt rounds)
- CORS habilitado para desenvolvimento
- Helmet.js para cabeçalhos de segurança HTTP
Backend (backend/.env)
PORT=3001
MONGO_URI=mongodb://mongo:27017/rn_chat
JWT_SECRET=change_me_super_secret_key_12345
JWT_EXPIRES_IN=15m
REFRESH_SECRET=change_me_refresh_secret_key_67890
REFRESH_EXPIRES_IN=7d
NODE_ENV=developmentFrontend (Específico da plataforma, veja frontend/src/shared/config/env.ts)
- iOS:
http://localhost:3001 - Android:
http://10.0.2.2:3001
- Iniciar backend:
./scripts/start-backend-docker.sh - Popular banco de dados:
./scripts/seed.sh - Iniciar frontend:
- iOS:
cd frontend && npm run ios - Android:
cd frontend && npm run android
- iOS:
- Fazer login com usuários de teste (alice, bob, charlie)
- Enviar mensagens em tempo real!
- Observar badges de mensagens não lidas atualizando automaticamente
MIT
Para problemas ou dúvidas, por favor abra uma issue no GitHub.
Bom Desenvolvimento! 🚀


