Open In App

Fantasy Sports App using MERN Stack

Last Updated : 03 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In today’s digital world, sports enthusiasts are always on the lookout for new and engaging ways to immerse themselves in their favorite games. Fantasy sports apps provide the perfect platform for fans to experience the thrill of team management, player selection, and match prediction. In this article, we’ll walk through the process of creating a Fantasy Sports App using the MERN stack.

Preview Image:

252

Prerequisites

Approach

  • Set up MongoDB database to store teams, players, and matches.
  • Create Express.js server to handle backend logic and API endpoints.
  • Develop React.js frontend to display teams, players, and matches data.
  • Implement functionality to fetch and display data from the backend.
  • Add feature to create fantasy teams by selecting players.

Steps to Create the Project:

Step 1: Create a new directory for your project using the following command.

mkdir fantasy-sports-app
cd fantasy-sports-app

Step 2: Create the server folder and initialize the Node application.

mkdir server
cd server
npm init -y

Step 3: Install necessary dependencies.

npm install express mongoose cors

Project Structure(Backend):

e3wretyhj

The updated Dependencies in package.json file of backend will look like:

"dependencies": {
    "cors": "^2.8.5",
    "express": "^4.18.3",
    "mongoose": "^8.2.2"
  }

Example: Create the required files as seen on folder structure and add the following codes.

JavaScript
// server.js

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
const routes = require('./routes/index');

const app = express();
const PORT = process.env.PORT || 5000;

app.use(bodyParser.json());
app.use(cors());

mongoose.connect('Your MongoDB URI', {
    useNewUrlParser: true,
    useUnifiedTopology: true,
});

const db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
db.once('open', () => {
    console.log('Connected to MongoDB');
});

app.use('/', routes);

app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});
JavaScript
// controllers/matchController.js

const Match = require('../models/match');

exports.getAllMatches = async (req, res) => {
    try {
        const matches = await Match.find();
        res.json(matches);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
};

exports.createMatch = async (req, res) => {
    const match = new Match({
        date: req.body.date,
        teams: req.body.teams,
        winner: req.body.winner,
    });

    try {
        const newMatch = await match.save();
        res.status(201).json(newMatch);
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
};
JavaScript
// controllers/teamController.js

const Team = require('../models/team');

exports.getAllTeams = async (req, res) => {
    try {
        const teams = await Team.find();
        res.json(teams);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
};

exports.createTeam = async (req, res) => {
    const team = new Team({
        name: req.body.name,
        players: req.body.players,
    });

    try {
        const newTeam = await team.save();
        res.status(201).json(newTeam);
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
};
JavaScript
// controllers/playerController.js

const Player = require('../models/player');

exports.getAllPlayers = async (req, res) => {
    try {
        const players = await Player.find();
        res.json(players);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
};

exports.createPlayer = async (req, res) => {
   const player = new Player({
        name: req.body.name,
        score: req.body.score,
        role: req.body.role
    });

    try {
        const newPlayer = await player.save();
        res.status(201).json(newPlayer);
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
};
JavaScript
// routes/index.js

const express = require('express');
const router = express.Router();
const matchController = require('../controllers/matchController');
const teamController = require('../controllers/teamController');
const playerController = require('../controllers/playerController');

router.get('/matches', matchController.getAllMatches);
router.post('/matches', matchController.createMatch);

router.get('/teams', teamController.getAllTeams);
router.post('/teams', teamController.createTeam);

router.get('/players', playerController.getAllPlayers);
router.post('/players', playerController.createPlayer);

module.exports = router;
JavaScript
// models/player.js

const mongoose = require("mongoose");

const Player = mongoose.model(
    "Player",
    new mongoose.Schema({
        name: { type: String, required: true },
        score: { type: Number, default: 0 },
        role: {
            type: String,
            enum: [
                "Batsman",
                "Bowler",
                "Wicketkeeper",
                "Captain",
                "Vice Captain",
                "Allrounder",
            ],
            required: true,
        },
    })
);

module.exports = Player;
JavaScript
// models/team.js

const mongoose = require("mongoose");

const Team = mongoose.model(
    "Team",
    new mongoose.Schema({
        name: { type: String, required: true },
        players: [{ type: mongoose.Schema.Types.ObjectId, ref: "Player" }],
    })
);

module.exports = Team;
JavaScript
// models/match.js

const mongoose = require("mongoose");

const Match = mongoose.model(
    "Match",
    new mongoose.Schema({
        date: { type: Date, required: true },
        teams: [{ type: mongoose.Schema.Types.ObjectId, ref: "Team" }],
        winner: { type: mongoose.Schema.Types.ObjectId, ref: "Team" },
    })
);

module.exports = Match;

To start the server run the following command.

node server.js

Steps to create Frontend

Step 1: Create a React app using:-

npx create-react-app frontend
cd frontend

Step 2: Install the required dependencies.

npm install axios

Project Structure(Frontend):

Screenshot-2024-03-13-180300

The updated Dependencies in package.json file of frontend will look like:

"dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.6.7",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  }

Example: Create the required files as shown in folder structure and add the following codes.

CSS
/* src/App.css */

body {
    margin: 1%;
    font-family: sans-serif;
    font-weight: 700;
    color: #283739;
    background-color: #99ddcc;
}


.header {
    text-align: center;
    margin-bottom: 40px;
}

.io {
    list-style-type: style none;
    ;
}

.header h1 {

    font-size: 28px;
    margin: 0;
}

.content {

    display: flex;

    flex-direction: row;
    justify-content: space-around;
}

.matches,
.teams,
.players,
.tea {
    background-color: #f7f7f2;
    padding: 5px;
    width: 100%;
    overflow: hidden;
    font-size: medium;
    margin: 1%;
    border-radius: 8px;
    border-radius: 20px;

}


.button {
    background-color: #00adb5;
    color: #ffffff;
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

.button:hover {
    background-color: #007580;
}

/* App.css */

.matches {
    background-color: #f8f9fa;
    border-radius: 8px;
    padding: 20px;

}

.team-creation input[type="checkbox"] {
    transform: scale(1.5);
    display: block;

}

.matches h2 {
    color: #00adb5;
    margin-top: 0;
}

.matches ul {
    list-style: none;
    padding: 0;
}

.matches li {
    padding: 10px 0;
    border-bottom: 1px solid #ddd;
}

.matches li:last-child {
    border-bottom: none;
}


.players h2 {
    color: #00adb5;
    margin-top: 0;
}

.players ul {
    list-style: none;
    padding: 0;
}

.players li {
    padding: 10px 0;
    border-bottom: 1px solid #ddd;
}

.players li:last-child {
    border-bottom: none;
}

.team-display {
    background-color: #f8f9fa;
    border-radius: 8px;
    padding: 20px;
    margin-top: 20px;
}

.team-display h2 {
    color: #00adb5;
    margin-top: 0;
}

.team-display ul {
    list-style: none;
    padding: 0;
}

.team-display li {
    padding: 10px 0;
    border-bottom: 1px solid #ddd;
}

.team-display li:last-child {
    border-bottom: none;
}

.team-creation {
    background-color: #f8f9fa;
    border-radius: 8px;
    padding: 20px;
    margin-top: 20px;
}

.team-creation h2 {
    color: #00adb5;
    margin-top: 0;
}

.team-creation label {
    display: block;
    margin-bottom: 10px;
}

.team-creation input[type="text"] {
    width: 100%;
    margin-bottom: 10px;
    padding: 8px;
    border: 1px solid #cccccc;
    border-radius: 4px;
    box-sizing: border-box;
}

.team-creation ul {
    list-style-type: none;
    padding: 0;
}



.team-creation button {
    background-color: #00adb5;
    color: #ffffff;
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

.team-creation button:hover {
    background-color: #007580;
}
Javascript
//Players.js

import React, { useState, useEffect } from "react";
import axios from "axios";

const Players = () => {
    const [players, setPlayers] = useState([]);
    const [loading, setLoading] = useState(true);
    const [formData, setFormData] = useState({
        name: "",
        score: 0,
        role: "",
    });

    useEffect(() => {
        fetchPlayers();
    }, []);

    const fetchPlayers = async () => {
        try {
            const response = await axios.get("http://localhost:5000/players");
            setPlayers(response.data);
            setLoading(false);
        } catch (error) {
            console.error("Error fetching players:", error);
            setLoading(false);
        }
    };

    const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData({
            ...formData,
            [name]: name === "score" ? parseInt(value) : value,
        });
    };

    const handleSubmit = async (e) => {
        e.preventDefault();
        try {
            await axios.post("http://localhost:5000/players", formData);
            setFormData({
                name: "",
                score: 0,
                role: "",
            });
            fetchPlayers();
        } catch (error) {
            console.error("Error creating player:", error);
        }
    };

    return (
        <div className="players" 
             style={{ maxWidth: "300px", margin: "0 auto" }}>
            <h2 style={{ marginBottom: "20px", marginTop: "25px" }}>
                Players
            </h2>
            <form onSubmit={handleSubmit} style={{ marginBottom: "20px" }}>
                <label
                    htmlFor="name"
                    style={{ display: "block", marginBottom: "10px" }}
                >
                    Player Name:
                </label>
                <input
                    type="text"
                    id="name"
                    name="name"
                    value={formData.name}
                    onChange={handleChange}
                    required
                    style={{
                        width: "100%",
                        padding: "8px",
                        fontSize: "16px",
                        borderRadius: "4px",
                        border: "1px solid #ccc",
                    }}
                />
                <label
                    htmlFor="score"
                    style={{ display: "block", 
                             marginBottom: "10px", 
                             marginTop: "10px" }}
                >
                    Score:
                </label>
                <input
                    type="number"
                    id="score"
                    name="score"
                    value={formData.score}
                    onChange={handleChange}
                    required
                    style={{
                        width: "100%",
                        padding: "8px",
                        fontSize: "16px",
                        borderRadius: "4px",
                        border: "1px solid #ccc",
                    }}
                />
                <label
                    htmlFor="role"
                    style={{ display: "block", 
                             marginBottom: "10px", 
                             marginTop: "10px" }}
                >
                    Role:
                </label>
                <input
                    type="text"
                    id="role"
                    name="role"
                    value={formData.role}
                    onChange={handleChange}
                    required
                    style={{
                        width: "100%",
                        padding: "8px",
                        fontSize: "16px",
                        borderRadius: "4px",
                        border: "1px solid #ccc",
                    }}
                />
                <button
                    type="submit"
                    style={{
                        marginTop: "10px",
                        padding: "8px 16px",
                        fontSize: "16px",
                        backgroundColor: "#1a8b9d",
                        color: "#fff",
                        border: "none",
                        borderRadius: "4px",
                        cursor: "pointer",
                    }}
                >
                    Create Player
                </button>
            </form>
            {loading ? (
                <p style={{ textAlign: "center" }}>Loading...</p>
            ) : players.length > 0 ? (
                <ul style={{ listStyleType: "none", padding: "0" }}>
                    {players.map((player) => (
                        <li
                            key={player._id}
                            style={{
                                marginBottom: "10px",
                                backgroundColor: "#f9f9f9",
                                padding: "10px",
                                borderRadius: "4px",
                                boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
                            }}
                        >
                            {player.name} - Score: {player.score}
                        </li>
                    ))}
                </ul>
            ) : (
                <p style={{ textAlign: "center" }}>No players available</p>
            )}
        </div>
    );
};

export default Players;
Javascript
//Matches.js

import React, { useState, useEffect } from "react";
import axios from "axios";

const Matches = () => {
    const [matches, setMatches] = useState([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        axios
            .get("http://localhost:5000/matches")
            .then((response) => {
                console.log("Matches data:", response.data);
                setMatches(response.data);
                setLoading(false);
            })
            .catch((error) => {
                console.error("Error fetching matches:", error);
                setLoading(false);
            });
    }, []);

    return (
        <div
            className="matches-container"
            style={{
                maxWidth: "600px",
                margin: "0 auto",
                padding: "20px",
                borderRadius: "8px",
                boxShadow: "0 4px 8px rgba(0, 0, 0, 0.1)",
                backgroundColor: "#f9f9f9",
            }}
        >
            <h1
                style={{
                    marginBottom: "20px",
                    fontSize: "24px",
                    color: "#1a8b9d",
                    fontSize: "50px",
                }}
            >
                Matches
            </h1>
            {loading ? (
                <p style={{ textAlign: "center", 
                            fontSize: "18px", 
                            color: "#555" }}>
                    Loading...
                </p>
            ) : Array.isArray(matches) && matches.length > 0 ? (
                <ul style={{ listStyleType: "none", padding: "0" }}>
                    {matches.map((match) => (
                        <li
                            key={match._id}
                            style={{
                                marginBottom: "15px",
                                padding: "10px",
                                borderRadius: "4px",
                                boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
                                backgroundColor: "#fff",
                            }}
                        >
                            <span
                                style={{
                                    fontSize: "18px",
                                    fontWeight: "bold",
                                    marginRight: "10px",
                                }}
                            >
                                {match.date}
                            </span>{" "}
                            -
                            <span>
                                {match.teams &&
                                    match.teams.length > 0 &&
                                    match.teams.map((team, index) => (
                                        <span key={index}>
                                            {index !== 0 && " vs "}
                                            {team.name}
                                        </span>
                                    ))}
                            </span>
                            <span style={{ marginLeft: "10px" }}>
                               Winner:{match.winner ? match.winner.name : "N/A"}
                            </span>
                        </li>
                    ))}
                </ul>
            ) : (
                <p style={{ textAlign: "center", 
                            fontSize: "18px", 
                            color: "#555" }}>
                    No matches available
                </p>
            )}
        </div>
    );
};

export default Matches;
Javascript
// Teams.js

import React, { useState, useEffect } from "react";
import axios from "axios";

const Team = () => {
    const [teams, setTeams] = useState([]);
    const [loading, setLoading] = useState(true);
    const [formData, setFormData] = useState({
        name: "",
    });

    useEffect(() => {
        axios
            .get("http://localhost:5000/teams")
            .then((response) => {
                setTeams(response.data);
                setLoading(false);
            })
            .catch((error) => {
                console.error("Error fetching teams:", error);
                setLoading(false);
            });
    }, []);

    const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData({
            ...formData,
            [name]: value,
        });
    };

    const handleSubmit = async (e) => {
        e.preventDefault();
        try {
            await axios.post("http://localhost:5000/teams", formData);
            setFormData({
                name: "",
            });
            const updatedResponse = await axios.get("http://localhost:5000/teams");
            setTeams(updatedResponse.data);
        } catch (error) {
            console.error("Error creating team:", error);
        }
    };

    return (
        <div
            className="team-display"
            style={{ maxWidth: "600px", margin: "0 auto" }}
        >
            <h2 style={{ marginBottom: "20px", marginTop: "17px" }}>Teams</h2>
            <form onSubmit={handleSubmit} style={{ marginBottom: "20px" }}>
                <label
                    htmlFor="name"
                    style={{ display: "block", marginBottom: "10px" }}
                >
                    Team Name:
                </label>
                <input
                    type="text"
                    id="name"
                    name="name"
                    value={formData.name}
                    onChange={handleChange}
                    required
                    style={{
                        width: "100%",
                        padding: "8px",
                        fontSize: "16px",
                        borderRadius: "4px",
                        border: "1px solid #ccc",
                    }}
                />
                <button
                    type="submit"
                    style={{
                        marginTop: "10px",
                        padding: "8px 16px",
                        fontSize: "16px",
                        backgroundColor: "#1a8b9d",
                        color: "#fff",
                        border: "none",
                        borderRadius: "4px",
                        cursor: "pointer",
                    }}
                >
                    Create Team
                </button>
            </form>
            {loading ? (
                <p style={{ textAlign: "center" }}>Loading teams...</p>
            ) : (
                <ul style={{ listStyleType: "none", padding: "0" }}>
                    {teams.map((team) => (
                        <li
                            key={team._id}
                            style={{
                                marginBottom: "10px",
                                backgroundColor: "#f9f9f9",
                                padding: "10px",
                                borderRadius: "4px",
                                boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
                            }}
                        >
                            {team.name}
                        </li>
                    ))}
                </ul>
            )}
        </div>
    );
};

export default Team;
Javascript
// TeamCreation.js

import React, { useState, useEffect } from "react";
import axios from "axios";

const TeamCreation = () => {
    const [players, setPlayers] = useState([]);
    const [selectedPlayers, setSelectedPlayers] = useState([]);
    const [teamName, setTeamName] = useState("");
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        axios
            .get("http://localhost:5000/players")
            .then((response) => {
                setPlayers(response.data);
                setLoading(false);
            })
            .catch((error) => {
                console.error("Error fetching players:", error);
                setLoading(false);
            });
    }, []);

    const handlePlayerSelection = (playerId) => {
        const selectedPlayer = players.find((player) => player._id === playerId);
        if (selectedPlayer) {
            const roleCount = selectedPlayers.filter(
                (player) => player.role === selectedPlayer.role
            ).length;
            const roleLimit = getRoleLimit(selectedPlayer.role);
            if (roleCount >= roleLimit) {
                console.log(
                    `You can select only ${roleLimit} ${selectedPlayer.role}s.`
                );
                return;
            }
            setSelectedPlayers([...selectedPlayers, selectedPlayer]);
        }
    };

    const getRoleLimit = (role) => {
        const roleLimits = {
            Batsman: 5,
            Bowler: 5,
            Wicketkeeper: 1,
            Captain: 1,
            "Vice Captain": 1,
            Allrounder: 2,
        };
        return roleLimits[role] || 0;
    };

    const handleTeamNameChange = (event) => {
        setTeamName(event.target.value);
    };

    const handleCreateTeam = () => {
        const requiredRoles = [
            "Batsman",
            "Bowler",
            "Wicketkeeper",
            "Captain",
            "Vice Captain",
            "Allrounder",
        ];
        for (const role of requiredRoles) {
            const roleCount = selectedPlayers.filter(
                (player) => player.role === role
            ).length;
            if (roleCount === 0) {
                alert(`Please select a ${role}.`);
                return;
            }
        }
        const newTeam = {
            name: teamName,
            players: selectedPlayers.map((player) => player._id),
        };
        axios
            .post("http://localhost:5000/teams", newTeam)
            .then((response) => {
                console.log("Team created:", response.data);
                setSelectedPlayers([]);
                setTeamName("");
            })
            .catch((error) => {
                console.error("Error creating team:", error);
            });
    };

    return (
        <div className="team-creation">
            <h2>Create Team</h2>
            {loading ? (
                <p>Loading data...</p>
            ) : (
                <div>
                    <label htmlFor="teamName">Team Name:</label>
                    <input
                        type="text"
                        id="teamName"
                        value={teamName}
                        onChange={handleTeamNameChange}
                    />
                    <ul>
                        {players.map((player) => (
                            <li key={player._id}>
                                <input
                                    type="checkbox"
                                    id={player._id}
                                    onChange={() => handlePlayerSelection(player._id)}
                                    disabled={
                                        selectedPlayers.filter((p) => p.role === player.role)
                                            .length >= getRoleLimit(player.role)
                                    } 
                                />
                                <label htmlFor={player._id}>
                                    {player.name} - Score: {player.score} - {player.role}
                                </label>
                            </li>
                        ))}
                    </ul>
                    <button onClick={handleCreateTeam}>Create Team</button>
                </div>
            )}
        </div>
    );
};

export default TeamCreation;
JavaScript
//App.js

import React from 'react';
import Matches from './Matches';
import Teams from './Teams';
import Players from './Players';
import './App.css';
import TeamCreation from './TeamCreation';

const App = () => {
    return (
        <div className="container">
            <div className="header">
                <h1>Fantasy Sports App</h1>
            </div>
            <div className="content">
                <div className="matches">
                    <Matches />
                </div>
                <div className="teams">
                    <Teams />
                </div>
                <div className="players">
                    <Players />
                </div>
                <div className="tea">
                    <TeamCreation />
                </div>
            </div>
        </div>
    );
};

export default App;


To start the application run the following command.

npm start

Output:

5434-ezgifcom-video-to-gif-converter



Like Article
Suggest improvement
Share your thoughts in the comments