Database
TrickBook uses MongoDB Atlas for data storage.
Connection
Database: TrickList2
Host: MongoDB Atlas (cloud)
const { MongoClient } = require("mongodb");
const ATLAS_URI = process.env.ATLAS_URI;
// mongodb+srv://user:pass@cluster.mongodb.net/TrickList2
const client = new MongoClient(ATLAS_URI);
await client.connect();
const db = client.db("TrickList2");
Each route file creates its own connection. Should use a centralized connection pool.
Collections
users
User accounts and subscription information.
{
_id: ObjectId,
name: String, // Display name
email: String, // Unique, used for login
password: String, // bcrypt hashed (null for SSO users)
imageUri: String, // S3 URL for profile picture
role: String, // "admin" or null
isGoogleSSO: Boolean, // true if registered via Google
appleUserId: String, // Apple Sign-In identifier
sports: [String], // Array of sport types
riderProfile: Object, // User's rider info
// Social features
network: Object, // User connections
homies: [ObjectId], // Array of friend user IDs
homieRequests: {
sent: [ObjectId], // Outgoing requests
received: [ObjectId] // Incoming requests
},
subscription: {
plan: String, // "free" or "premium"
status: String, // "active", "canceled", "past_due"
stripeCustomerId: String,
stripeSubscriptionId: String,
currentPeriodEnd: Date,
lastPaymentDate: Date,
adminOverride: Boolean // For testing
},
createdAt: Date,
updatedAt: Date
}
Indexes:
email(unique)
tricklists
User's personal trick lists.
{
_id: ObjectId,
name: String, // List name
user: ObjectId, // Reference to users._id
completed: Number, // Count of checked tricks
tricks: [
{
_id: ObjectId,
name: String,
checked: Boolean
}
]
}
Relationships:
user→users._id
tricks
Individual tricks (legacy collection, embedded in tricklists).
{
_id: ObjectId,
list_id: String, // Reference to tricklists._id
name: String,
checked: Boolean
}
trickipedia
Global trick encyclopedia (admin-managed).
{
_id: ObjectId,
name: String, // Trick name
category: String, // e.g., "Flip Tricks", "Grinds"
difficulty: String, // e.g., "Beginner", "Intermediate"
description: String, // Full description
steps: [String], // Step-by-step instructions
images: [String], // S3 URLs
videoUrl: String, // YouTube/external video
source: String, // Attribution
url: String, // URL slug for SEO
createdAt: Date,
updatedAt: Date
}
Indexes:
url(unique)categoryname(text index for search)
spotlists
User's spot list collections.
{
_id: ObjectId,
name: String,
description: String,
userId: String, // User's ObjectId as string
spotIds: [ObjectId], // References to spots
createdAt: Date,
updatedAt: Date
}
Relationships:
userId→users._idspotIds[]→spots._id
spots
Skate spot locations.
{
_id: ObjectId,
name: String,
latitude: Number,
longitude: Number,
images: [String], // Array of image URLs
description: String,
rating: Number, // 0-5
tags: String, // Comma-separated
city: String,
state: String,
sportTypes: [String], // skateboarding, bmx, etc.
category: String, // park, street, indoor, diy, other
userId: ObjectId, // Creator reference
createdAt: Date,
updatedAt: Date
}
Indexes:
- Geospatial index on
{latitude, longitude}(recommended)
feed_posts
Social feed posts with media content.
{
_id: ObjectId,
userId: ObjectId, // Creator reference
caption: String,
description: String,
mediaType: String, // "video" or "image"
mediaUrl: String, // CDN URL
thumbnailUrl: String, // Video thumbnail
tricks: [String], // Associated trick names
stats: {
loveCount: Number,
respectCount: Number,
commentCount: Number,
shareCount: Number,
viewCount: Number
},
engagement: {
completionRate: Number
},
createdAt: Date,
updatedAt: Date
}
reactions
Love/respect reactions on feed posts.
{
_id: ObjectId,
userId: ObjectId,
postId: ObjectId,
type: String, // "love" or "respect"
createdAt: Date
}
feed_comments
Comments on feed posts.
{
_id: ObjectId,
postId: ObjectId,
userId: ObjectId,
content: String,
createdAt: Date
}
saved_posts
User bookmarked posts.
{
_id: ObjectId,
userId: ObjectId,
postId: ObjectId,
createdAt: Date
}
conversations
Direct message conversations.
{
_id: ObjectId,
participants: [ObjectId], // User IDs
lastMessage: Object, // Preview of last message
createdAt: Date,
updatedAt: Date
}
dm_messages
Individual direct messages.
{
_id: ObjectId,
conversationId: ObjectId,
senderId: ObjectId,
content: String,
read: Boolean,
createdAt: Date
}
blog
Website blog posts.
{
_id: ObjectId,
title: String,
author: String,
date: Date,
content: String, // HTML or Markdown
url: String, // URL slug
images: [String] // S3 URLs
}
Indexes:
url(unique)
categories
Trick categories for the app.
{
_id: ObjectId,
name: String, // e.g., "Flip Tricks"
icon: String, // Icon name for MaterialCommunityIcons
backgroundColor: String, // Hex color
color: String // Text color
}
expoPushTokens
Push notification tokens.
{
_id: ObjectId,
token: String, // ExponentPushToken[xxx]
userId: ObjectId
}
messages
Contact form submissions.
{
_id: ObjectId,
name: String,
email: String,
message: String,
createdAt: Date
}
Data Relationships
users
│
├──< tricklists (user field)
│ │
│ └──< tricks (embedded or list_id)
│
├──< spotlists (userId field)
│ │
│ └──< spots (spotIds array)
│
├──< feed_posts (userId field)
│ │
│ ├──< reactions (postId field)
│ ├──< feed_comments (postId field)
│ └──< saved_posts (postId field)
│
├──< conversations (participants array)
│ │
│ └──< dm_messages (conversationId field)
│
├──< homies (users.homies array → users._id)
│
└──< expoPushTokens (userId field)
trickipedia (standalone, admin-managed)
blog (standalone, admin-managed)
categories (standalone, admin-managed)
Query Examples
Get User's Trick Lists with Tricks
const tricklists = await db.collection("tricklists")
.find({ user: new ObjectId(userId) })
.toArray();
Search Trickipedia
const tricks = await db.collection("trickipedia")
.find({
$or: [
{ name: { $regex: searchTerm, $options: "i" } },
{ description: { $regex: searchTerm, $options: "i" } }
],
category: categoryFilter,
difficulty: difficultyFilter
})
.toArray();
Get Spots in List
const spotlist = await db.collection("spotlists")
.findOne({ _id: new ObjectId(listId) });
const spots = await db.collection("spots")
.find({ _id: { $in: spotlist.spotIds } })
.toArray();
Update Subscription
await db.collection("users").updateOne(
{ _id: new ObjectId(userId) },
{
$set: {
"subscription.plan": "premium",
"subscription.status": "active",
"subscription.stripeSubscriptionId": subscriptionId,
"subscription.currentPeriodEnd": new Date(periodEnd * 1000)
}
}
);
Recommended Indexes
// users
db.users.createIndex({ email: 1 }, { unique: true });
// tricklists
db.tricklists.createIndex({ user: 1 });
// trickipedia
db.trickipedia.createIndex({ url: 1 }, { unique: true });
db.trickipedia.createIndex({ category: 1 });
db.trickipedia.createIndex({ name: "text", description: "text" });
// spotlists
db.spotlists.createIndex({ userId: 1 });
// spots (for geospatial queries)
db.spots.createIndex({ latitude: 1, longitude: 1 });
// blog
db.blog.createIndex({ url: 1 }, { unique: true });
Backup Strategy
MongoDB Atlas provides:
- Continuous backups
- Point-in-time recovery
- Snapshot backups
Access via Atlas dashboard → Backup → Snapshots.