Skip to main content

Database Models

G-RANK uses MongoDB with Mongoose 9 as the ODM. The database contains four main collections.


User​

Stored in the users collection.

FieldTypeRequiredDescription
usernameStringYesUnique display name
emailStringYesUnique email address
passwordStringYesBcrypt-hashed password
roleStringYes"USER" or "ADMIN" (default: "USER")
mmrNumberYesCurrent MMR rating (default: 0)
winsNumberYesTotal wins (default: 0)
lossesNumberYesTotal losses (default: 0)
emailVerifiedBooleanYesWhether email is confirmed (default: false)
emailVerificationTokenStringNoToken for email verification link
emailVerificationExpiresDateNoExpiry for verification token
passwordResetTokenStringNoToken for password reset link
passwordResetExpiresDateNoExpiry for reset token
riotIdStringNoLinked Riot account ID
riotGameNameStringNoRiot game name (e.g., Player#EUW)
riotTagLineStringNoRiot tag line
riotAccessTokenStringNoRiot RSO access token (encrypted/stored carefully)
riotRefreshTokenStringNoRiot RSO refresh token
createdAtDateAutoMongoose timestamp
updatedAtDateAutoMongoose timestamp

Schema snippet:

const userSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true, trim: true },
email: { type: String, required: true, unique: true, lowercase: true },
password: { type: String, required: true },
role: { type: String, enum: ['USER', 'ADMIN'], default: 'USER' },
mmr: { type: Number, default: 0 },
wins: { type: Number, default: 0 },
losses: { type: Number, default: 0 },
emailVerified: { type: Boolean, default: false },
// ...verification/reset token fields
// ...riot fields
}, { timestamps: true });

Indexes:

  • email — unique index
  • username — unique index
  • mmr — descending index for leaderboard queries

Lobby​

Stored in the lobbies collection.

FieldTypeRequiredDescription
nameStringYesLobby display name
gameStringYesGame name (e.g., "League of Legends")
createdByObjectIdYesReference to users collection
maxParticipantsNumberYesMaximum number of players allowed
participantCountNumberYesCached count (default: 0)
statusStringYes"open", "closed", or "completed" (default: "open")
descriptionStringNoOptional lobby description
createdAtDateAutoMongoose timestamp
updatedAtDateAutoMongoose timestamp

Schema snippet:

const lobbySchema = new mongoose.Schema({
name: { type: String, required: true },
game: { type: String, required: true },
createdBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
maxParticipants: { type: Number, required: true },
participantCount:{ type: Number, default: 0 },
status: { type: String, enum: ['open', 'closed', 'completed'], default: 'open' },
description: { type: String },
}, { timestamps: true });

Indexes:

  • status — for filtering open lobbies
  • createdBy — for user's created lobbies query
  • game — for filtering by game

LobbyParticipant​

Stored in the lobbyparticipants collection. Represents the many-to-many relationship between users and lobbies.

FieldTypeRequiredDescription
lobbyObjectIdYesReference to lobbies collection
userObjectIdYesReference to users collection
joinedAtDateYesWhen the user joined (default: Date.now)
createdAtDateAutoMongoose timestamp
updatedAtDateAutoMongoose timestamp

Schema snippet:

const lobbyParticipantSchema = new mongoose.Schema({
lobby: { type: mongoose.Schema.Types.ObjectId, ref: 'Lobby', required: true },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
joinedAt: { type: Date, default: Date.now },
}, { timestamps: true });

Indexes:

  • { lobby, user } — compound unique index (prevents duplicate registrations)
  • user — for querying a user's joined lobbies

MatchResult​

Stored in the matchresults collection.

FieldTypeRequiredDescription
lobbyObjectIdYesReference to lobbies
submittedByObjectIdYesReference to users (who submitted)
winnerObjectIdYesReference to users (who won)
replayUrlStringYesValidated replay URL
gameStringYesGame identifier
mmrChangesArrayNoArray of { user, oldMmr, newMmr, delta } objects
createdAtDateAutoMongoose timestamp

Schema snippet:

const matchResultSchema = new mongoose.Schema({
lobby: { type: mongoose.Schema.Types.ObjectId, ref: 'Lobby', required: true },
submittedBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
winner: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
replayUrl: { type: String, required: true },
game: { type: String, required: true },
mmrChanges: [{
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
oldMmr: Number,
newMmr: Number,
delta: Number,
}],
}, { timestamps: true });

MongoDB Atlas Setup​

G-RANK targets MongoDB Atlas for production. The connection is established via the MONGO_URI environment variable:

MONGO_URI=mongodb+srv://<user>:<password>@<cluster>.mongodb.net/<dbname>?retryWrites=true&w=majority

For local development, you can also use:

MONGO_URI=mongodb://localhost:27017/grank

The connection is initialized in backend/src/config/db.js (or similar) using:

import mongoose from 'mongoose';

export const connectDB = async () => {
await mongoose.connect(process.env.MONGO_URI);
console.log('MongoDB connected');
};