DevTinder Database Design and Schema MongoDB Collections and Relationships
Learn the complete database design for DevTinder MongoDB collections, Mongoose schemas, relationships between users, connection requests, and messages, and indexing strategies.
DevTinder Database Design and Schema
The DevTinder database is built on MongoDB with Mongoose as the ODM. This guide covers all collections, their schemas, relationships, and indexing strategies.
Collections Overview
DevTinder has three main collections:
- users User profiles (name, email, password, photo, skills, about)
- connectionrequests Connection requests between users (from, to, status)
- messages Chat messages between connected users (sender, receiver, text)
User Schema
const userSchema = new mongoose.Schema({ firstName: { type: String, required: true, trim: true, minLength: 2, maxLength: 50 }, lastName: { type: String, trim: true, maxLength: 50 }, email: { type: String, required: true, unique: true, lowercase: true, trim: true, validate(value) { if (!validator.isEmail(value)) { throw new Error("Invalid email"); } } }, password: { type: String, required: true, minLength: 8, validate(value) { if (!validator.isStrongPassword(value)) { throw new Error("Password too weak"); } } }, photoUrl: { type: String, default: "https://default-avatar.png" }, age: { type: Number, min: 18 }, gender: { type: String, enum: ["male", "female", "other"] }, about: { type: String, maxLength: 500, default: "Tell us about yourself" }, skills: { type: [String], validate: { validator: function(skills) { return skills.length <= 10; }, message: "Cannot have more than 10 skills" } } }, { timestamps: true });
Key features:
- Email validation with
validatorpackage - Strong password validation
- Unique index on email (automatic via
unique: true) - Timestamps for createdAt and updatedAt
- Array field for skills with max length validation
ConnectionRequest Schema
const connectionRequestSchema = new mongoose.Schema({ fromUserId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }, toUserId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }, status: { type: String, required: true, enum: ["interested", "ignored", "accepted", "rejected"] } }, { timestamps: true }); // Compound unique index prevents duplicate requests connectionRequestSchema.index( { fromUserId: 1, toUserId: 1 }, { unique: true } );
Key features:
- ObjectId references to User collection
- Status enum with four possible values
- Compound unique index prevents duplicate requests between the same pair
- Timestamps for tracking when the request was sent and reviewed
Message Schema
const messageSchema = new mongoose.Schema({ senderId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }, receiverId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }, text: { type: String, required: true, maxLength: 1000 } }, { timestamps: true }); // Index for efficient message history queries messageSchema.index({ senderId: 1, receiverId: 1, createdAt: -1 });
Key features:
- References to both sender and receiver
- Text field with max length
- Compound index on senderId, receiverId, and createdAt for efficient pagination of message history
Relationships
The relationships are:
- User → ConnectionRequest One-to-many (a user can send/receive many requests)
- User → Message One-to-many (a user can send/receive many messages)
- ConnectionRequest → User Many-to-one (each request references two users)
These are implemented using ObjectId references and populate() to fetch related documents.
Indexing Strategy
Indexes are critical for performance as the user base grows:
| Collection | Index | Purpose |
|---|---|---|
| users | email (unique) | Fast login lookups, prevent duplicates |
| connectionrequests | fromUserId + toUserId (unique) | Prevent duplicate requests |
| connectionrequests | toUserId + status | Fast query for pending requests |
| messages | senderId + receiverId + createdAt | Fast message history pagination |
The Takeaway
The DevTinder database has three collections: users (profiles), connectionrequests (connections), and messages (chat). Each schema uses proper validation, references (ref) for relationships, and strategic indexes for performance. The compound unique index on connection requests prevents duplicates, and the message index enables efficient pagination of chat history.
Three collections: users (user profiles with name, email, password, photo, skills, about), connectionrequests (connection requests with from, to, status), and messages (chat messages with sender, receiver, text).
Using ObjectId references (ref) in Mongoose. ConnectionRequest references two users (fromUserId, toUserId). Message references sender and receiver. Use populate() to fetch the full user document when needed.
The compound unique index on fromUserId and toUserId prevents duplicate connection requests between the same pair of users at the database level, ensuring data integrity even if the application logic fails.
A compound index on senderId, receiverId, and createdAt (descending). This optimizes the most common query fetching message history between two users in reverse chronological order with pagination.
Email validation with the validator package (isEmail), strong password validation (isStrongPassword), minimum age of 18, max 10 skills, trimmed and length-limited name fields, and unique lowercase email.
Ready to master Node.js completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master Node.js
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

