⚽ Futbol Chat FULLSTACK
Real-time Chat APP with React, Nestjs GraphQL, WebSockets & Advanced Caching

Links
Created
Project README
Detailed documentation about this project
Technologies
Overview
A real-time fullstack chat application supporting public and private rooms, user profiles, and advanced message caching. Built with NestJS GraphQL for the backend and React for the frontend, featuring WebSocket subscriptions for instant messaging and Redis for message caching.
What & Why
What
A real-time chat app for Argentine soccer fans. Public and private chat rooms where you can talk about teams, players, or whatever you want with other fans.
Why
I've been working with NestJS for a while now, so I felt comfortable using it for the backend. Same with GraphQL—I really enjoy working with it and it fits perfectly for real-time features like subscriptions. Since I love soccer, it only made sense to build something around that. My projects always tend to have some kind of connection to soccer, it's just what I like.
The Challenge
Building a chat that's fast, smooth, and handles many users connected at the same time. Also managing real-time user presence and making sure everything scales well.
Highlights
- Layered Architecture
- SOLID Principles + Design Patterns
- Cache-Aside Pattern with Redis (50-100ms faster)
- Cursor-Based Pagination
- JWT Authentication + bcrypt hashing
- HTTP-only Secure Cookies
- Distributed Rate Limiting (Redis-backed)
- GraphQL + WebSockets (real-time features)
- Live User Presence tracking with Redis Sets
- Typing Indicators with efficient broadcasting
- Pub/Sub Architecture for scalable event distribution
- Structured Logging (Winston for Dev and Prod environments)
- Correlation IDs for Request Tracing
- Performance Metrics
- Custom Error Formatting
- Redis: Pub/Sub for real-time updates, Rate-limiting using Redis tokens, Caching frequently fetched data
Features
- Real-time messaging with WebSocket subscriptions (GraphQL)
- Create and manage public and private chat rooms
- User authentication and profile management
- Cache-Aside Pattern with Redis
- Cursor-Based Pagination for messages
- Redis Pub/Sub for real-time events - Typing indicators - Live User Precense
- Room administration - invite users, edit settings, delete rooms
- User search and friend-like system for private rooms
- Message persistence with PostgreSQL
- JWT Authentication
- Rate Limiting (Redis backed)
- Structured Logging with Winston (Dev and Prod environments)
- Correlation IDs for Request Tracing
- Performance Metrics
- Error Handler
- For the Frontend - React Compiler (automatically applies memoization)
- User registration with validation
- Secure login with JWT tokens
- Profile updates (including avatar upload)
- User search functionality
- Session management with refresh tokens
- Add/remove members with access control
- Chatroom metadata (name, description, color, image)
- List chatrooms per user with caching
- Delete chatrooms with cascade cleanup
- Send text messages with validation
- Image upload via GraphQL multipart
- Paginated message history (cursor-based)
- Real-time message delivery via subscriptions
- Message timestamps with timezone support
- Live User Presence - Track active users per chatroom
- User Events - Join/leave notifications
- Connection Management - WebSocket lifecycle hooks
Architecture
Type: Layered Architecture with NestJS
Redis is the backbone of the system: Pub/Sub for real-time updates, Rate-limiting using Redis tokens, Caching frequently fetched data. Because Redis operates in memory, the performance gain was huge.
Principles
- SOLID Principles
- Design Patterns
- Cache-Aside Pattern
- Event-Driven Architecture
Layers
modules/
Feature modules (auth, user, chatroom, live-chatroom, PubSub)
common/
Shared utilities (cache, constants, guards, interceptors, middleware, plugins, throttler, logger)
Performance Optimizations
| Optimization | Implementation |
|---|---|
| Cache-Aside Pattern | Redis caching with TTL for frequently accessed data - 50-100ms faster |
| Cursor-Based Pagination | API returns limited items with cursor pointing to next batch - reduces payload size |
| Redis Pub/Sub | Real-time event distribution across multiple instances |
| Rate Limiting | @nestjs/throttler with Redis storage - different limits per operation type |
| Structured Logging | Winston with JSON in production, human-readable in dev |
| Correlation IDs | Request tracing across all services |
Security
| Feature | Implementation |
|---|---|
| JWT Authentication | Stateless authentication tokens with @nestjs/jwt |
| Password Hashing | bcrypt for secure password storage |
| Input Validation | class-validator decorators |
| Custom Guards | File-grained authorization |
| HTTP-only Cookies | Secure cookie storage for tokens |
| Rate Limiting | Redis-backed distributed rate limiting |
Project Structure
Type: NestJS Module-based
src/modules/auth/- Authentication & JWTsrc/modules/user/- User managementsrc/modules/chatroom/- Chatroom operationssrc/modules/live-chatroom/- Real-time trackingsrc/modules/PubSub/- Redis Pub/Sub setupsrc/common/cache/- Redis caching servicesrc/common/constants/- Constants for Inject dependenciessrc/common/filter/- GraphQL exception filtersrc/common/guards/- Custom Guardssrc/common/interceptors/- Request interceptorssrc/common/middleware/- Custom middlewaresrc/common/plugins/- Logger Plugin to catch graphql contextsrc/common/throttler/- GraphQLThrottlerGuardsrc/common/token/- TokenService - verify refresh tokensrc/common/types/- GraphQLExecutionTypessrc/common/interfaces/- GraphQLContext, ILogger, etcsrc/common/logger/- Logging configurationsrc/common/utils/- Helper functionsprisma/- Database schema and migrationsInstallation
Clone repositorygit clone https://github.com/ValentinZoia/chatapp-backend.gitInstall dependenciesnpm installSetup environment variablescp .env.example .env.developmentConfigure .env.developmentDATABASE_URL, REDIS_URL, JWT_SECRETRun database migrationsnpm run prisma:migrateOptionalSeed database: npm run prisma:seedStart development servernpm run start:dev (before start all docker services)Start all services with Dockerdocker-compose up -d
Learnings
While building this app, I gained deep hands-on experience with several advanced concepts that textbooks rarely cover. Authentication & Security Implementing a complete refresh token rotation system with HTTP only cookies taught me about CSRF protection, cookie security flags (SameSite, Secure, HttpOnly). GraphQL Deep Dives Working with GraphQL subscriptions reveled challenges I had't anticipated. Setting up Redis Pub/Sub for scaling WebSockets connections across multiple instances meant understanding separete publisher/subscriber connections and implementing reconnection strategies. I also learned how implementing a cursor-based pagination for messages, that is essential for real-time chat applications. Custom GraphQL scalars (like DateTime and Upload) taugght me how to transform data at the schema level, and implementing file uploads via streams helped me understand Node.js streams deeply. Error Handling in GraphQL This was a game-changer. Unlike REST where HTTP status codes communicate errors, GraphQL requires a completely different approach. I built a custom GraphQlExceptionFilter that transforms HTTP exceptions into structured GraphQL errors with correlation IDs-essential for debugging in production. Learning to differentiate between programming errors (that should hide details from users) and operational errors (validation failures, auth issues) was crucial. Observability Implementing Winston logging with environment-aware formats (JSON in production, human-readable in dev) taught me about strucutred logging. Adding correlation ID propagation through all requests (via middleware and GraphQL context) that allow me to trace requests across services easily, improving debugging and monitoring and gave me a clearly idea for the ExecutionContext and his lifecycle (Interceptors -> Apollo Plugins -> Resolver). The Apollo plugin system for logging the entire GraphQL request lifecycle, have a entire Full control over GraphQL l... (line truncated to 2000 chars)
Challenges
- I encountered several real obstacles that forced me to rethink parts of the architecture and implementation. One of the first challenges I faced was handling real-time presence at scale. Tracking which users are online in each chatroom initially seemed straightforward, but the complexity quickly appeared once I considered multiple server instances. In a horizontally scaled environment, a user connected to Server A wouldn't be visible to someone connected to Server B, which breaks the illusion of real-time presence. To solve this, I implemented Redis Sets to track online users per chatroom. When a user connects via WebSocket, their ID is added to a Redis set keyed by the chatroom ID. Because all server instances query this centralized data source, the distributed state problem is effectively resolved. The trade-off is a slight latency on presence updates, but in return I get consistency across all instances, which is far more important in a distributed system. Another important issue appeared when implementing rate limiting for the GraphQL API. NestJS's built-in throttler works very well for REST endpoints, but GraphQL introduces a different constraint: everything goes through a single /graphql endpoint. This means that, by default, all operations share the same rate limit bucket, which becomes a major vulnerability. For example, a user could exhaust the limit with harmless queries and block legitimate mutations. To address this, I built a custom GraphQLThrottlerGuard that inspects the operation type—Query, Mutation, or Subscription—and generates rate-limit keys based on both the client's IP address and the authenticated user ID. This approach allows different limits for different operation types while also preventing authenticated users from bypassing limits anonymously. As the system started supporting real-time updates, I ran into another subtle problem related to GraphQL subscriptions. Since subscriptions maintain persistent co... (line truncated to 2000 chars)