Back to Projects
🃏

FullStack Casino Platform

Production-quality 12-game online casino with MVC architecture and security hardening

Node.jsExpress.jsMySQLJavaScriptChart.jsbcrypthelmetMVC
12
Casino Games
67
Unit Tests
30+
API Endpoints
6
UI Themes

Project Overview

Built as a software engineering portfolio project demonstrating advanced Node.js/Express architecture, MySQL database design, session management, real-time game state persistence, and modern vanilla JavaScript UI patterns. The platform features 12 casino games across table, card, machine, and instant categories — each with correct rule implementations and server-side state. Platform systems include atomic chip transactions with SELECT FOR UPDATE race condition prevention, a data-driven achievement engine (adding achievements = inserting a DB row), quadratic XP leveling, Chart.js bankroll/outcome charts, 5-category leaderboards, streak-based daily rewards, 6 CSS-variable theme system, and a full Blackjack AI coach with 6-deck S17 basic strategy and EV calculator.

The Problem

Building a multi-game casino platform requires correct rule implementations per game, secure atomic chip accounting, scalable session management, and meaningful player progression — all without introducing race conditions or security vulnerabilities.

The Solution

Designed an MVC + Service Layer architecture where game engines are pure functions isolated from the database. Instant games use a shared service that atomically handles bet validation, chip deduction, history, stats, XP, and achievements. Session games persist JSON state in game_sessions for mid-hand reconnection. All chip operations use SELECT FOR UPDATE transactions.

Key Highlights

  • 12 games: Blackjack, European Roulette, Baccarat, Craps, Texas Hold'em, Video Poker, High/Low, Slots, Mines, Keno, Dice, Coin Flip
  • MVC + Service Layer: game engines are pure functions with zero DB/HTTP access — fully unit-testable
  • 67 unit tests covering Card, Hand, Deck, and Shoe classes in the game engine core
  • MySQL session store with session fixation prevention (req.session.regenerate() on login)
  • Atomic chip operations using SELECT FOR UPDATE inside transactions — no race conditions
  • Data-driven achievement engine: adding achievements requires inserting a DB row, zero code changes
  • Blackjack AI coach: full 6-deck S17 basic strategy table + EV calculator + win probability display
  • Chart.js statistics dashboard: bankroll history, outcome breakdown, win/loss streaks
  • 5-category leaderboards: chips, level, wins, blackjacks, streak
  • 6 UI themes via CSS custom property overrides: Classic, Dark, Las Vegas, Neon, Royal Gold, Minimal
  • Security: prepared statements, express-validator XSS sanitization, helmet CSP, timing-attack-safe auth, rate limiting

Data / Request Flow

🖥️
Client
HTML/CSS/JS
🚦
Express
Router
🎛️
Controller
Thin layer
⚙️
Service
Business logic
🎮
Game Engine
Pure functions
🗄️
MySQL 8.0
Persistence

System Architecture

01.Client: HTML/CSS/Vanilla JS (ES Modules) organized into api/, components/, core/, games/, and pages/ directories
02.Server: Node.js + Express 4.x with MVC controllers → services → models (MySQL) pattern
03.Game engines: pure functions under server/game-engine/ — no DB access, no HTTP, easily unit-tested
04.Database: MySQL 8.0 with mysql2 prepared statements throughout (SQL injection prevention)
05.Auth: bcrypt + express-session + express-mysql-session; session fixation prevention on login
06.Security: helmet (CSP), express-rate-limit, express-validator, CORS, multer for uploads
07.Schema: users, profiles, player_levels, statistics, game_sessions (UUID PK), game_history (immutable ledger), transactions (audit trail), achievements (data-driven catalog), user_achievements
08.Testing: 67 unit tests on core game engine classes (Card, Hand, Deck, Shoe)

Code Spotlight

Real snippets from the codebase demonstrating key engineering decisions.

chip-transaction.service.js
javascript
1// Atomic chip operation — SELECT FOR UPDATE prevents race conditions
2async function processGameResult(userId, betAmount, outcome) {
3  return await db.transaction(async (trx) => {
4    // Lock the row for this transaction
5    const [profile] = await trx('profiles')
6      .where({ user_id: userId })
7      .forUpdate()  // SELECT FOR UPDATE
8      .select('chips');
9
10    if (profile.chips < betAmount)
11      throw new Error('Insufficient chips');
12
13    const payout = outcome.win ? betAmount * outcome.multiplier : 0;
14    const delta  = payout - betAmount;
15
16    await trx('profiles')
17      .where({ user_id: userId })
18      .increment('chips', delta);
19
20    // Immutable audit ledger
21    await trx('transactions').insert({
22      user_id: userId, amount: delta,
23      type: outcome.win ? 'win' : 'loss',
24      game: outcome.game, created_at: new Date(),
25    });
26
27    return { newBalance: profile.chips + delta, payout };
28  });
29}
blackjack.engine.js
javascript
1// Pure function — no DB access, no HTTP. Fully unit-testable.
2function getBasicStrategyAction(playerHand, dealerUpcard, deckCount = 6) {
3  const total  = playerHand.hardTotal();
4  const isSoft = playerHand.isSoft();
5  const isPair = playerHand.isPair();
6
7  if (isPair) return PAIR_STRATEGY[playerHand.cards[0].rank][dealerUpcard.rank];
8  if (isSoft) return SOFT_STRATEGY[total][dealerUpcard.rank];
9  return HARD_STRATEGY[total][dealerUpcard.rank];
10}
11
12function calculateEV(playerHand, dealerUpcard, betAmount) {
13  const action    = getBasicStrategyAction(playerHand, dealerUpcard);
14  const winProb   = WIN_PROBABILITY_TABLE[playerHand.hardTotal()][dealerUpcard.rank];
15  const ev        = (winProb * betAmount) - ((1 - winProb) * betAmount);
16  return { action, ev: ev.toFixed(2), winProbability: (winProb * 100).toFixed(1) };
17}

Challenges & How I Solved Them

1

Preventing chip race conditions when multiple requests arrive simultaneously — solved with SELECT FOR UPDATE inside MySQL transactions

2

Implementing timing-attack-safe login (dummy bcrypt hash for non-existent users)

3

Building a data-driven achievement system that works for any achievement type without code changes

4

Managing mid-hand reconnection for session-based games (Blackjack, Roulette, Mines, Hold'em)

5

Keeping 12 different game rule implementations correct and maintainable

Results

  • 12 fully playable casino games with correct rule sets and server-side state management
  • 67 passing unit tests on the core game engine (Card, Hand, Deck, Shoe)
  • Security hardened: prepared statements, CSP, rate limiting, session fixation prevention, XSS sanitization
  • Complete player progression: XP/levels, data-driven achievements, Chart.js dashboard, 5-category leaderboards

Future Improvements

Add WebSocket support for live multiplayer Blackjack tablesImplement provably fair RNG with cryptographic verificationAdd Texas Hold'em multiplayer lobbyBuild admin dashboard for user management and game analyticsDeploy with Docker + CI/CD pipeline