Twenty-one Game using MEAN Stack
Last Updated :
23 Jul, 2025
Twenty-one Game is a popular card game played in casinos worldwide. In this article, we'll explore how to create a Twenty-one game also known as the BlackJack game using the MEAN stack (MongoDB, Express.js, Angular, and Node.js). By building a Twenty-one game, we'll see the various aspects of web development with the MEAN stack.
Project Preview:
Twenty-one Game using MEAN Stack
Prerequisites
Approach to Create the Game
- Set up project structure with client and server folders.
- Create Express.js routes for game actions.
- Develop Angular components for the game UI.
- Implement Blackjack game logic with JavaScript.
- Apply CSS styling for a visually appealing interface.
Steps to Create the Project
Step 1: Create the project folder using the following command.
mkdir blackjack-mean
cd blackjack-mean
Step 2: Create a server folder
mkdir server
cd server
Step 3: Initialize the node application using the following command.
npm init -y
Folder Structure(Backend)
Backend Folder StructureStep 4: Create the required files as shown in folder structure.
Step 5: Install required dependencies
npm install express mongoose cors body-parser path
Updated dependencies will look like
"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.19.2",
"mongoose": "^8.3.2",
"path": "^0.12.7"
}
Example
JavaScript
//index.js
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const path = require('path');
const { setupGameRoutes } = require('./game');
const { connectToDatabase } = require('./db');
const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors());
app.use(bodyParser.json());
// Connect to the database
connectToDatabase();
// Setup game routes
setupGameRoutes(app);
// Serve static files from the Angular app
app.use(express.static(path
.join(__dirname, '..', 'client', 'dist')));
app.get('*', (req, res) => {
res.sendFile(path
.join(__dirname, '..', 'client', 'dist', 'index.html'));
});
// Start the server
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
JavaScript
//game.js
const { Card, Player, Game } = require('./db');
// Helper function to create a new deck
function createDeck() {
const suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades'];
const ranks = ['2', '3', '4', '5', '6', '7',
'8', '9', '10', 'J', 'Q', 'K', 'A'];
const deck = [];
for (const suit of suits) {
for (const rank of ranks) {
const card = new Card({
suit: suit,
rank: rank,
value: rank === 'A' ? 11 :
isNaN(rank) ? 10 : parseInt(rank),
});
deck.push(card);
}
}
return deck;
}
// Helper function to calculate the score of a hand
function calculateScore(hand) {
let score = hand.reduce((total, card) =>
total + card.value, 0);
// Handle Aces (reduce value from 11 to 1 if necessary)
hand.filter(card => card.rank === 'A')
.forEach(_ => {
if (score > 21) {
score -= 10;
}
});
return score;
}
// Helper function to determine the winner
function determineWinner(playerScore, dealerScore) {
if (playerScore > 21) {
return 'Dealer'; // Player busts, dealer wins
}
if (dealerScore > 21) {
return 'Player'; // Dealer busts, player wins
}
if (playerScore > dealerScore) {
return 'Player'; // Player has a higher score
} else if (playerScore < dealerScore) {
return 'Dealer'; // Dealer has a higher score
} else {
return 'Draw'; // Scores are equal, it's a draw
}
}
// Setup game routes
function setupGameRoutes(app) {
// Endpoint to start a new game
app.post('/game/start', async (req, res) => {
try {
const newDeck = createDeck();
// Shuffle the deck (Fisher-Yates algorithm)
for (let i = newDeck.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[newDeck[i], newDeck[j]] = [newDeck[j], newDeck[i]];
}
const newGame = new Game({
deck: newDeck,
player: { name: 'Player', hand: [], score: 0 },
dealer: { name: 'Dealer', hand: [], score: 0 },
});
// Deal the initial two cards to the player and the dealer
newGame.player.hand.push(newGame.deck.pop());
newGame.dealer.hand.push(newGame.deck.pop());
newGame.player.hand.push(newGame.deck.pop());
newGame.dealer.hand.push(newGame.deck.pop());
// Update scores
newGame.player.score = calculateScore(newGame.player.hand);
newGame.dealer.score = calculateScore(newGame.dealer.hand);
// Save the new game to the database
await newGame.save();
res.status(201).json(newGame);
} catch (error) {
console.error(error);
res.status(500).send('Internal Server Error');
}
});
// Endpoint to handle player hits
app.post('/game/hit', async (req, res) => {
try {
const gameId = req.body.gameId;
// Fetch the game from the database
const game = await Game.findById(gameId);
// Draw a card from the deck and add it to the player's hand
const drawnCard = game.deck.pop();
game.player.hand.push(drawnCard);
// Update the player's score
game.player.score = calculateScore(game.player.hand);
// Set the winner field
game.winner = determineWinner(game.player.score, game.dealer.score);
// Save the updated game to the database
await game.save();
res.json({ ...game.toObject(), winner: game.winner });
} catch (error) {
console.error(error);
res.status(500).send('Internal Server Error');
}
});
// Endpoint to handle player standing
app.post('/game/stand', async (req, res) => {
try {
const gameId = req.body.gameId;
// Fetch the game from the database
const game = await Game.findById(gameId);
// Dealer draws cards until their score is 17 or higher
while (game.dealer.score < 17) {
const drawnCard = game.deck.pop();
game.dealer.hand.push(drawnCard);
game.dealer.score = calculateScore(game.dealer.hand);
}
// Set the winner field
game.winner = determineWinner(game.player.score, game.dealer.score);
// Save the updated game to the database
await game.save();
res.json({ ...game.toObject(), winner: game.winner });
} catch (error) {
console.error(error);
res.status(500).send('Internal Server Error');
}
});
}
module.exports = { createDeck, calculateScore, determineWinner, setupGameRoutes };
JavaScript
//db.js
const mongoose = require('mongoose');
// Database connection configuration
function connectToDatabase() {
mongoose.connect('mongodb://localhost:27017/blackjack', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
}
const cardSchema = new mongoose.Schema({
suit: String,
rank: String,
value: Number,
});
const playerSchema = new mongoose.Schema({
name: String,
hand: [cardSchema],
score: Number,
});
const gameSchema = new mongoose.Schema({
deck: [cardSchema],
player: playerSchema,
dealer: playerSchema,
winner: String,
});
const Card = mongoose.model('Card', cardSchema);
const Player = mongoose.model('Player', playerSchema);
const Game = mongoose.model('Game', gameSchema);
module.exports = { connectToDatabase, Card, Player, Game };
To start the server run the following command.
node server.js
Step 6: Install Angular CLI if you haven't already in the root folder.
npm install -g @angular/cli
Step 7: Initialize a new Angular project.
ng new client
cd client
Folder Structure(Frontend)
Frontend Folder StructureStep 8: Install the required dependencies
npm install axios
npm install @angular/common @angular/compiler @angular/core @angular/forms
@angular/platform-browser @angular/platform-browser-dynamic
@angular/router rxjs tslib zone.js
Updated dependencies will look like:
"dependencies": {
"@angular/animations": "^17.3.0",
"@angular/common": "^17.3.5",
"@angular/compiler": "^17.3.5",
"@angular/core": "^17.3.5",
"@angular/forms": "^17.3.5",
"@angular/platform-browser": "^17.3.5",
"@angular/platform-browser-dynamic": "^17.3.5",
"@angular/router": "^17.3.5",
"axios": "^1.6.8",
"rxjs": "~7.8.0",
"tslib": "^2.6.2",
"zone.js": "^0.14.4"
},
Example: Create the required files as shown in the folder structure and add the following codes.
HTML
<!-- src/app/game/game.component.html -->
<div class="navbar">
<h1>GFG Twenty-one Game</h1>
</div>
<div class="kl">
<h1>Twenty-one Game</h1>
<p class="winner-message">{{ winnerMessage }}</p>
<div class="ma">
<div class="playerside">
<h2>Player Hand:</h2>
<ul>
<li *ngFor="let card of gameState?.player.hand">
{{ card.rank }} of {{ card.suit }}
</li>
</ul>
<p>Score: {{ gameState?.player.score }}</p>
</div>
<div class="dealerside">
<h2>Dealer Hand:</h2>
<ul>
<li *ngFor="let card of gameState?.dealer.hand">
{{ card.rank }} of {{ card.suit }}
</li>
</ul>
<p>Score: {{ gameState?.dealer.score }}</p>
</div>
</div>
<div class="buttons">
<button (click)="handleHit()">Hit</button>
<button (click)="handleStand()">Stand</button>
<button (click)="startNewGame()">Start New Game</button>
</div>
</div>
HTML
<!-- app.component.html -->
<app-game></app-game>
HTML
<!-- index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>BlackjackClient</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
CSS
/* src/app/game/game.component.css */
body {
background-color: #00203F;
color: #ADEFD1;
font-family: sans-serif;
}
.navbar {
background-color: #00203F;
color: green;
padding: 15px 0;
text-align: center;
}
.navbar h1 {
margin: 0;
font-size: 24px;
font-weight: bold;
}
.kl {
text-align: center;
padding: 20px;
}
ul {
list-style-type: none;
padding: 0;
}
button {
background-color: #efd8ad;
color: #00203F;
border: none;
padding: 10px 20px;
margin: 5px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #00203F;
color: #ade2ef;
}
.winner-message {
color: #FF5555;
font-size: 30px;
font-weight: bolder;
margin-top: 20px;
animation: fadeIn 1s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.ma {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
}
h2,
p {
color: #00203F;
}
.playerside {
padding: 2%;
background-color: #adcfef;
color: #00203F;
width: fit-content;
margin: 2%;
border-radius: 35px;
}
.dealerside {
width: fit-content;
margin: 2%;
border-radius: 35px;
padding: 2%;
background-color: #adceef;
color: #00203F;
}
JavaScript
//src/app/game/game.component.ts
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-game',
templateUrl: './game.component.html',
styleUrls: ['./game.component.css']
})
export class GameComponent implements OnInit {
gameState: any;
winnerMessage: string = '';
constructor(private http: HttpClient) { }
ngOnInit(): void {
this.startNewGame();
}
startNewGame(): void {
this.http.post<any>('http://localhost:5000/game/start', {})
.subscribe(response => {
this.gameState = response;
this.winnerMessage = '';
}, error => console
.error('Error starting a new game:', error));
}
handleHit(): void {
this.http.post<any>('http://localhost:5000/game/hit',
{ gameId: this.gameState._id })
.subscribe(response => {
this.gameState = response;
this.checkWinner(response.winner);
}, error => console
.error('Error hitting:', error));
}
handleStand(): void {
this.http.post<any>('http://localhost:5000/game/stand',
{ gameId: this.gameState._id })
.subscribe(response => {
this.gameState = response;
this.checkWinner(response.winner);
}, error => console.error('Error standing:', error));
}
checkWinner(winner: string): void {
this.winnerMessage = `Winner: ${winner}`;
setTimeout(() => {
this.startNewGame();
}, 3000);
}
}
JavaScript
//app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
}
JavaScript
//app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { GameComponent } from './game/game.component';
@NgModule({
declarations: [
AppComponent,
GameComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
JavaScript
//app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [];
JavaScript
//main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
In another terminal, navigate to the client folder and start the Angular development server.
ng serve
Open your web browser and go to http://localhost:4200 to access the MEAN Blackjack game.
Output:
