DEV Community

Ravi Bhuvan
Ravi Bhuvan

Posted on

Project Explanation for my Chesso application

Project

High-Level Summary

Chesso is a full-stack, real-time multiplayer chess platform engineered to provide low-latency online gameplay. It features real-time move synchronization, authoritative backend match clocks, secure authentication using JWT and Google OAuth, and full chess rule validation. I built it using the MERN stack (MongoDB, Express, React, Node.js) combined with Socket.IO for bi-directional WebSocket communication and Chess.js for move validation and FEN (Forsyth–Edwards Notation) state management. One of the main challenges I solved was building a server-authoritative state and clock synchronization mechanism to prevent client tampering and handle mid-game reconnections gracefully.

Tech Stack & Architectural Overview

  • Frontend : React, Vite for fast builds, React for declarative UI updates upon WebSocket events.
  • Backend API : Node.js, Express.js Event-driven, non-blocking I/O ideal for handling multiple concurrent WebSocket connections.
  • Socket.IO : Provides bi-directional socket events, auto-reconnection fallback, and socket room abstraction.
  • Database : MongoDB for flexible JSON-like document model ideal for storing FEN strings, match logs, and user metadata.
  • Auth & Security : Google OAuth 2.0, Passport.js, JWT, bcryptusing standard authentication flow providing password hashing (bcrypt) and session security via JWT tokens.
  1. Implementation of matchmaking & Queueing System

    • When a player clicks "Play", the client emits StartGame with their playerID.
    • The server verifies turn ownership (game.currentP === playerID).
    • The move is executed in an isolated server-side Chess() instance (gameSockets.js).
    • If empty, the player is queued and notified via waitingForOpponent.
    • If another player is waiting, waitingQ.shift() pairs them instantly, creates a new game record in MongoDB (GameModel.js), assigns piece colors (white/black), and joins both sockets into a dedicated Socket.IO room named after the gameID.
  2. Game Recovery & Reconnection Resilience

    • The server exposes a recoverGame socket event (gameSockets.js).
    • If an active game exists in Node's memory, the socket re-joins the room and emits recoverGameState.
    • If the Node instance restarted, the state is hydrated dynamically from MongoDB using the saved board FEN string (new Chess(DbGame.boardState)).
  3. What was the hardest technical challenge in this project?

    • Handling chess timers in real-time requires strict concurrency control. If timers ran strictly on the client, users could tamper with local timers
    • I solved this by building backend-driven interval timers per active room. Every time a move occurs, the active timer stops, the remaining time is updated in memory and DB, and the next player's timer fires while broadcasting synced timestamp snapshots to both clients.
  4. Why did you use Socket.IO instead of traditional HTTP REST APIs or pure WebSockets?

  • For turn-based real-time games like chess, HTTP polling incurs high latency and unnecessary overhead because HTTP headers are sent with every request. Socket.IO provides full-duplex, persistent TCP connections for bi-directional streaming.
  • I chose Socket.IO over raw WebSockets because Socket.IO provides out-of-the-box abstractions like Rooms (allowing two players to be isolated into a single gameID room), automatic reconnection fallback mechanisms (polling if WebSockets are blocked by proxies), and simple event-based broadcasting.
  1. Walk me through your database schemas.
  • User Schema (UserModels.js): Stores user credentials (Name, email, password). It includes a pre-save hook using bcrypt to hash passwords automatically, and custom methods like comparepassword() and generateAccessToken() for JWT authentication.
  • Game Schema (GameModel.js): References player1 and player2 via Mongoose ObjectId references to User. It tracks boardState (FEN string), currentP, status ('waiting', 'ongoing', 'finished'), WinnerID, Result ('Time-Out', 'Resignation', 'CheckMate', 'Draw'), and the remaining timer snapshot.
  1. How would you scale Chesso to handle 100,000 active concurrent chess matches?
  • Stateless Backend Nodes + Redis Adapter: Spin up multiple Node.js socket servers behind an NGINX / AWS ALB load balancer. Connect all socket nodes using the @socket.io/redis-adapter so sockets across different servers can communicate.
  • Distributed In-Memory Store (Redis): Move active game states and timer counters into Redis Cache. Redis data structures (e.g., Hashes) allow sub-millisecond atomic reads/writes for game state updates.
  1. What is OAuth 2.0, how the end-to-end flow works?
  • At a high level, OAuth 2.0 is a secure delegation protocol that allows users to authenticate using a trusted third-party provider like Google without exposing their password to our application. In Chesso, I implemented the Authorization Code Grant Flow with Passport.js, bridged with an internal JWT authentication system.
    • First, when a user clicks 'Sign in with Google', the frontend hits my backend endpoint at /auth/google (AuthRoutes.js). This triggers passport. authenticate('google') which redirects the user’s browser to Google’s OAuth 2.0 consent screen, requesting the profile and email scopes.
    • Second, once the user approves, Google redirects back to my registered backend callback URL at /auth/google/callback with a one-time Authorization Code.
    • Third, behind the scenes inside passport.js, Passport automatically exchanges that code with Google's servers for the user's profile data (email, displayName, photo). My callback strategy then checks MongoDB for an existing user record. If it’s their first time logging in, I automatically upsert a new User document with their Google details.
    • Fourth—and this is a key architectural decision— instead of passing Google’s token back to the frontend, my controller in AuthControllers.js generates our own custom, short-lived JWT access token via user.generateAccessToken().
  1. how did u come with a System Design for this project?

When designing Chesso, my primary goal was to build an ultra-low-latency, server-authoritative system that prevents client-side cheating while handling disconnects gracefully

  • Protocol & State Representation
    • Standard HTTP polling introduces too much overhead for turn-based games, so I chose Socket.IO for full-duplex WebSocket communication. To keep payload sizes lightweight (~60 bytes), I represented the full board state using FEN strings (Forsyth–Edwards Notation) rather than sending heavy move arrays.
  • Dual-Layer Storage Architecture
    • Active games and backend Node.js setInterval clocks live in memory for sub-millisecond move processing and timer synchronization and Every valid move updates a persistent record in MongoDB. If a user refreshes or a server restarts, the recoverGame socket event.
  • Server-Authoritative Logic
    • All move validations and game clocks run strictly on the backend via chess.js and Node intervals. The client is purely a view layer, eliminating local timer drift or move manipulation.

Top comments (0)