Database Schema
Database models and relationships
Database Schema Documentation
Complete reference for the DBK Gaming Platform database schema.
Overview
The platform uses a shared Prisma schema that supports both:
- SQLite (Development)
- PostgreSQL (Production)
Schema Location: prisma/schema.prisma
Tables
Guild
Represents Discord servers that have the bot installed.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Discord guild ID | Primary Key |
dashboardChannel | String? | Channel ID for admin dashboard | Optional |
dashboardMessage | String? | Message ID for admin dashboard | Optional |
userDashboardChannel | String? | Channel ID for user dashboard | Optional |
userDashboardMessage | String? | Message ID for user dashboard | Optional |
logChannelId | String? | Channel ID for audit logs | Optional |
description | String? | Server description for web panel | Optional |
region | String? | Geographic region (e.g., NA, EU, APAC) | Optional |
isOfficial | Boolean | Official DBK server flag | Default: false |
isFeatured | Boolean | Featured on web panel homepage | Default: false |
isApproved | Boolean | Server listing approved | Default: true |
addedBy | String? | Discord ID of user who added server | Optional |
createdAt | DateTime | Creation timestamp | Auto-generated |
updatedAt | DateTime | Last update timestamp | Auto-updated |
Relations:
games: One-to-many with Gamereviews: One-to-many with ServerReviewmembers: One-to-many with ServerMembergameServers: One-to-many with GameServer
Indexes:
isOfficial: Fast filtering of official serversisApproved: Fast filtering of approved serversisFeatured: Fast filtering of featured servers
Game
Represents games managed by the bot within a Discord server.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Unique game identifier | Primary Key, CUID |
guildId | String | Discord guild ID | Foreign Key → Guild |
name | String | Game name (e.g., "Valorant") | Required |
emoji | String? | Emoji icon for the game | Optional |
setupMode | String | Setup mode: "simple" or "advanced" | Required |
adminRoleId | String | Discord role ID for game admins | Required |
gameRoleId | String | Discord role ID for game members | Required |
plusRoleId | String? | Discord role ID for Plus members | Optional |
categoryId | String | Discord category ID | Required |
announceChannelId | String? | Announcement channel ID | Optional |
textChannels | String | JSON array of text channel IDs | Required |
voiceChannels | String | JSON array of voice channel IDs | Required |
forumChannels | String? | JSON array of forum channel IDs | Optional |
plusChannels | String? | JSON array of Plus-only channel IDs | Optional |
archived | Boolean | Game is archived (soft delete) | Default: false |
archivedAt | DateTime? | Archival timestamp | Optional |
createdAt | DateTime | Creation timestamp | Auto-generated |
updatedAt | DateTime | Last update timestamp | Auto-updated |
Relations:
guild: Many-to-one with Guild
Unique Constraints:
(guildId, name): One game per name per server
Indexes:
guildIdarchived
User
Represents users who have logged into the web panel.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Discord user ID | Primary Key |
username | String | Discord username | Required, Unique |
discriminator | String? | Discord discriminator (legacy) | Optional |
avatar | String? | Discord avatar hash | Optional |
bio | String? | User bio (max 500 characters) | Optional |
favoriteGames | String? | Comma-separated favorite games | Optional |
socialLinks | String? | JSON object of social media links | Optional |
role | String | User role: "MEMBER" or "ADMIN" | Default: "MEMBER" |
accessToken | String? | Encrypted Discord OAuth access token | Optional |
refreshToken | String? | Encrypted Discord OAuth refresh token | Optional |
tokenExpiry | DateTime? | OAuth token expiry timestamp | Optional |
createdAt | DateTime | Account creation timestamp | Auto-generated |
updatedAt | DateTime | Last update timestamp | Auto-updated |
Relations:
sessions: One-to-many with Sessionannouncements: One-to-many with Announcementreviews: One-to-many with ServerReviewnotifications: One-to-many with Notificationrank: One-to-one with UserRankpermissions: One-to-many with PermissionserverMemberships: One-to-many with ServerMemberforumThreads: One-to-many with ForumThreadforumPosts: One-to-many with ForumPostforumReactions: One-to-many with ForumPostReactiongameServers: One-to-many with GameServerliveChatMessages: One-to-many with LiveChatMessage
Indexes:
username: Unique index for fast lookuprole: Fast filtering by role (deprecated in favor of UserRank)createdAt: Fast sorting by join date
Social Links Format:
{
"twitter": "@username",
"twitch": "username",
"youtube": "channelname",
"steam": "steamid"
}
Session
Represents active user sessions for the web panel.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Session identifier | Primary Key, CUID |
userId | String | Discord user ID | Foreign Key → User |
token | String | Session token | Required, Unique |
expiresAt | DateTime | Session expiry timestamp | Required |
createdAt | DateTime | Session creation timestamp | Auto-generated |
Relations:
user: Many-to-one with User
Indexes:
userIdexpiresAt
Session Expiry: 7 days from creation
Announcement
Represents announcements displayed on the web panel homepage.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Announcement identifier | Primary Key, CUID |
title | String | Announcement title | Required |
body | String | Announcement content (Markdown) | Required |
authorId | String | Discord ID of announcement author | Foreign Key → User |
published | Boolean | Published status (visible to public) | Default: false |
createdAt | DateTime | Creation timestamp | Auto-generated |
updatedAt | DateTime | Last update timestamp | Auto-updated |
Relations:
author: Many-to-one with User
Indexes:
(published, createdAt): Composite index for efficient queries
ServerReview
Represents user reviews/ratings for Discord servers.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Review identifier | Primary Key, CUID |
serverId | String | Discord guild ID | Foreign Key → Guild |
userId | String | Discord user ID | Foreign Key → User |
rating | Int | Rating (1-5) | Required |
comment | String? | Review comment | Optional |
createdAt | DateTime | Creation timestamp | Auto-generated |
updatedAt | DateTime | Last update timestamp | Auto-updated |
Relations:
server: Many-to-one with Guilduser: Many-to-one with User
Unique Constraints:
(serverId, userId): One review per user per server
Indexes:
serverId
Notification
Represents user notifications in the web panel.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Notification identifier | Primary Key, CUID |
userId | String | Discord user ID | Foreign Key → User |
type | String | Notification type | Required |
title | String | Notification title | Required |
body | String? | Notification body text | Optional |
read | Boolean | Read status | Default: false |
createdAt | DateTime | Creation timestamp | Auto-generated |
Relations:
user: Many-to-one with User
Indexes:
(userId, read): Composite index for unread queriescreatedAt
Notification Types:
SERVER_APPROVED: Server listing approvedSERVER_FEATURED: Server featured on homepageANNOUNCEMENT_POSTED: New announcement publishedGAME_ADDED: Game successfully added to serverGAME_ARCHIVED: Game archived by admin
AuditLog
Represents bot action audit trail (game management operations).
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Log entry identifier | Primary Key, CUID |
guildId | String | Discord guild ID | Required |
userId | String | Discord user ID of actor | Required |
action | String | Action type | Required |
targetId | String? | Target resource ID | Optional |
metadata | String? | JSON metadata | Optional |
createdAt | DateTime | Action timestamp | Auto-generated |
Indexes:
(guildId, createdAt): Composite index
Action Types:
GAME_CREATED: Game setup completedGAME_ARCHIVED: Game archivedGAME_DELETED: Game permanently deletedDASHBOARD_SETUP: Dashboard created
AdminAuditLog
Represents admin action audit trail (web panel operations).
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Log entry identifier | Primary Key, CUID |
adminId | String | Discord ID of admin | Required |
action | String | Action type | Required |
targetType | String? | Target resource type | Optional |
targetId | String? | Target resource ID | Optional |
metadata | String? | JSON metadata | Optional |
createdAt | DateTime | Action timestamp | Auto-generated |
Indexes:
(adminId, createdAt): Composite indexcreatedAt
Action Types:
UPDATE_USER_ROLE: User role changedUPDATE_USER_RANK: User rank changedGRANT_PERMISSION: Permission granted to userREVOKE_PERMISSION: Permission revoked from userDELETE_USER: User account deletedFEATURE_SERVER: Server featured/unfeaturedAPPROVE_SERVER: Server approved/rejectedCREATE_ANNOUNCEMENT: Announcement createdUPDATE_ANNOUNCEMENT: Announcement updatedDELETE_ANNOUNCEMENT: Announcement deletedDELETE_FORUM_POST: Forum post deletedDELETE_FORUM_THREAD: Forum thread deletedAPPROVE_GAME_SERVER: Game server approvedREJECT_GAME_SERVER: Game server rejected
CustomActivity
Represents custom bot activity status.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Activity identifier | Primary Key |
name | String | Activity name | Required |
type | Int | Activity type (0=Playing, 3=Watching, etc.) | Required |
url | String? | Streaming URL (if type=1) | Optional |
createdBy | String | Discord ID of creator | Required |
createdAt | DateTime | Creation timestamp | Auto-generated |
expiresAt | DateTime? | Expiry timestamp | Optional |
isActive | Boolean | Active status | Default: true |
Indexes:
isActiveexpiresAt
Activity Types:
0: Playing1: Streaming (requires URL)2: Listening to3: Watching5: Competing in
UserRank
Represents user rank/hierarchy level in the platform.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Rank identifier | Primary Key, CUID |
userId | String | Discord user ID | Foreign Key → User, Unique |
rank | String | Rank name (MEMBER, MODERATOR, ADMIN) | Default: "MEMBER" |
level | Int | Numeric level (1-100) | Default: 1 |
grantedBy | String? | Discord ID of user who granted rank | Optional |
grantedAt | DateTime | Timestamp when rank was granted | Auto-generated |
Relations:
user: One-to-one with User
Unique Constraints:
userId: One rank per user
Indexes:
rank: Fast filtering by rank typelevel: Fast sorting by level
Rank Hierarchy:
MEMBER(Level 1): Default rank for all usersMODERATOR(Level 50): Can moderate content and manage usersADMIN(Level 100): Full platform access
Permission
Represents granular user permissions (overrides rank defaults).
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Permission identifier | Primary Key, CUID |
userId | String | Discord user ID | Foreign Key → User |
permission | String | Permission name | Required |
granted | Boolean | Permission granted (true) or revoked (false) | Default: true |
scope | String? | Optional scope (e.g., categoryId, serverId) | Optional |
grantedBy | String? | Discord ID of admin who granted permission | Optional |
createdAt | DateTime | Creation timestamp | Auto-generated |
Relations:
user: Many-to-one with User
Unique Constraints:
(userId, permission, scope): One permission per user per scope
Indexes:
userId: Fast lookup of user permissionspermission: Fast filtering by permission type
Permission Types:
MANAGE_SERVERS: Manage server listingsMANAGE_USERS: Manage user accountsMANAGE_ANNOUNCEMENTS: Create/edit announcementsMODERATE_FORUM: Moderate forum contentAPPROVE_GAME_SERVERS: Approve game server submissionsDELETE_CONTENT: Delete user content
ServerMember
Represents per-server membership and roles.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Membership identifier | Primary Key, CUID |
serverId | String | Discord guild ID | Foreign Key → Guild |
userId | String | Discord user ID | Foreign Key → User |
rank | String | Server-specific rank | Default: "MEMBER" |
joinedAt | DateTime | Membership creation timestamp | Auto-generated |
Relations:
server: Many-to-one with Guilduser: Many-to-one with User
Unique Constraints:
(serverId, userId): One membership per user per server
Indexes:
serverId: Fast lookup of server membersuserId: Fast lookup of user memberships
Server Ranks:
MEMBER: Basic server accessMODERATOR: Can manage server settingsADMIN: Full server control
ForumCategory
Represents forum categories/sections.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Category identifier | Primary Key, CUID |
name | String | Category name | Required |
slug | String | URL-friendly slug | Required, Unique |
description | String? | Category description | Optional |
icon | String? | Icon/emoji for category | Optional |
position | Int | Display order position | Default: 0 |
minReadRank | String | Minimum rank to read threads | Default: "MEMBER" |
minWriteRank | String | Minimum rank to create threads | Default: "MEMBER" |
createdAt | DateTime | Creation timestamp | Auto-generated |
updatedAt | DateTime | Last update timestamp | Auto-updated |
Relations:
threads: One-to-many with ForumThreadpermissions: One-to-many with ForumCategoryPermission
Unique Constraints:
slug: Unique category URL slug
Indexes:
position: Fast sorting by display orderslug: Fast lookup by slug
ForumThread
Represents forum discussion threads.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Thread identifier | Primary Key, CUID |
categoryId | String | Category ID | Foreign Key → ForumCategory |
authorId | String | Thread author Discord ID | Foreign Key → User |
title | String | Thread title | Required |
slug | String | URL-friendly slug | Required |
content | String | Thread content (Markdown) | Required |
isPinned | Boolean | Pinned to top of category | Default: false |
isLocked | Boolean | Locked (no new posts) | Default: false |
viewCount | Int | Number of views | Default: 0 |
createdAt | DateTime | Creation timestamp | Auto-generated |
updatedAt | DateTime | Last update timestamp | Auto-updated |
Relations:
category: Many-to-one with ForumCategoryauthor: Many-to-one with Userposts: One-to-many with ForumPost
Unique Constraints:
(categoryId, slug): Unique slug per category
Indexes:
(categoryId, isPinned, createdAt): Fast thread listing with pinned first(categoryId, isPinned, updatedAt): Fast thread listing sorted by activityauthorId: Fast lookup of user's threadscreatedAt: Fast sorting by creation date
ForumPost
Represents forum thread replies/posts.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Post identifier | Primary Key, CUID |
threadId | String | Thread ID | Foreign Key → ForumThread |
authorId | String | Post author Discord ID | Foreign Key → User |
content | String | Post content (Markdown) | Required |
isDeleted | Boolean | Soft delete flag | Default: false |
deletedBy | String? | Discord ID of moderator who deleted | Optional |
deletedAt | DateTime? | Deletion timestamp | Optional |
createdAt | DateTime | Creation timestamp | Auto-generated |
updatedAt | DateTime | Last update timestamp | Auto-updated |
Relations:
thread: Many-to-one with ForumThreadauthor: Many-to-one with Userreactions: One-to-many with ForumPostReaction
Indexes:
(threadId, createdAt): Fast post listing in chronological orderauthorId: Fast lookup of user's posts
ForumPostReaction
Represents emoji reactions to forum posts.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Reaction identifier | Primary Key, CUID |
postId | String | Post ID | Foreign Key → ForumPost |
userId | String | Discord user ID | Foreign Key → User |
emoji | String | Emoji character/code | Required |
createdAt | DateTime | Creation timestamp | Auto-generated |
Relations:
post: Many-to-one with ForumPostuser: Many-to-one with User
Unique Constraints:
(postId, userId, emoji): One reaction per emoji per user per post
Indexes:
postId: Fast lookup of post reactionsuserId: Fast lookup of user reactions
ForumCategoryPermission
Represents per-category permission overrides.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Permission identifier | Primary Key, CUID |
categoryId | String | Category ID | Foreign Key → ForumCategory |
rank | String | Rank this permission applies to | Required |
canRead | Boolean | Can read threads in category | Default: true |
canWrite | Boolean | Can create threads/posts | Default: true |
canModerate | Boolean | Can pin/lock/delete | Default: false |
createdAt | DateTime | Creation timestamp | Auto-generated |
Relations:
category: Many-to-one with ForumCategory
Unique Constraints:
(categoryId, rank): One permission set per rank per category
Indexes:
categoryId: Fast lookup of category permissions
GameServer
Represents submitted game servers for community listing.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Server identifier | Primary Key, CUID |
name | String | Server name | Required |
gameType | String | Game type (minecraft, csgo, etc.) | Required |
ip | String | Server IP address | Required |
port | Int | Server port | Required |
description | String? | Server description | Optional |
website | String? | Server website URL | Optional |
tags | String? | Comma-separated tags | Optional |
submittedBy | String | Discord ID of submitter | Foreign Key → User |
linkedGuildId | String? | Linked Discord guild ID | Foreign Key → Guild |
isApproved | Boolean | Approval status | Default: false |
approvedBy | String? | Discord ID of approver | Optional |
approvedAt | DateTime? | Approval timestamp | Optional |
isActive | Boolean | Server is active | Default: true |
lastOnline | DateTime? | Last time server was online | Optional |
createdAt | DateTime | Submission timestamp | Auto-generated |
updatedAt | DateTime | Last update timestamp | Auto-updated |
Relations:
submitter: Many-to-one with UserlinkedGuild: Many-to-one with Guild (optional)status: One-to-many with GameServerStatus
Indexes:
gameType: Fast filtering by game type(isApproved, isActive): Fast filtering of approved/active serverssubmittedBy: Fast lookup of user submissionslinkedGuildId: Fast lookup of guild servers
Game Types:
minecraft: Minecraft serverscsgo: Counter-Strike: Global Offensiverust: Rustark: ARK: Survival Evolvedvalheim: Valheimterraria: Terraria
GameServerStatus
Represents historical status snapshots of game servers.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Status record identifier | Primary Key, CUID |
serverId | String | Game server ID | Foreign Key → GameServer |
isOnline | Boolean | Server online status | Required |
playerCount | Int? | Current player count | Optional |
maxPlayers | Int? | Maximum player slots | Optional |
map | String? | Current map name | Optional |
version | String? | Server version | Optional |
ping | Int? | Response time in ms | Optional |
rawData | String? | Raw query response (JSON) | Optional |
timestamp | DateTime | Query timestamp | Auto-generated |
Relations:
server: Many-to-one with GameServer
Indexes:
(serverId, timestamp): Fast lookup of server historytimestamp: Fast cleanup of old records
Retention: Records older than 48 hours are automatically deleted
LiveChatMessage
Represents real-time chat messages on the homepage.
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Message identifier | Primary Key, CUID |
userId | String | Discord user ID | Foreign Key → User |
content | String | Message content | Required |
isDeleted | Boolean | Soft delete flag | Default: false |
deletedBy | String? | Discord ID of moderator who deleted | Optional |
deletedAt | DateTime? | Deletion timestamp | Optional |
createdAt | DateTime | Creation timestamp | Auto-generated |
Relations:
user: Many-to-one with User
Indexes:
createdAt: Fast retrieval of recent messagesuserId: Fast lookup of user messages
Retention: Recent 100 messages are kept in memory for real-time display
LiveChatSettings
Stores configuration for Discord webhook integration with the live chat system. Uses singleton pattern (single row with id = "singleton").
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Settings identifier | Primary Key, Default: "singleton" |
enabled | Boolean | Enable/disable Discord sync | Default: false |
webhookUrl | String? | Discord webhook URL | Optional |
guildId | String? | Discord server ID | Optional |
channelId | String? | Discord channel ID | Optional |
createdAt | DateTime | Creation timestamp | Auto-generated |
updatedAt | DateTime | Last update timestamp | Auto-updated |
Relations:
- None (singleton configuration model)
Purpose:
- Enables syncing web chat messages to Discord via webhook
- Configured through admin panel by users with MANAGE_LIVECHAT_SETTINGS or MANAGE_SYSTEM permission
- Messages sent to live chat widget are forwarded to configured Discord channel with user's username and avatar
Singleton Pattern:
- Always accessed with id = "singleton"
- Only one configuration allowed per system
- Created on first access if not exists
AMPServer
Represents official game servers managed through CubeCoders AMP (Application Management Panel).
| Field | Type | Description | Constraints |
|---|---|---|---|
id | String | Server identifier | Primary Key, CUID |
ampInstanceId | String | AMP instance ID (from AMP API) | Required, Unique |
instanceName | String | Server name from AMP | Required |
gameType | String | Game type (minecraft, rust, ark, etc.) | Required |
ip | String | Server IP address | Required |
port | Int | Server port | Required |
isRunning | Boolean | Server running status | Default: false |
playerCount | Int? | Current player count | Optional |
maxPlayers | Int? | Maximum player slots | Optional |
uptime | Int? | Server uptime in seconds | Optional |
lastSync | DateTime | Last sync with AMP API | Auto-updated |
createdAt | DateTime | First discovery timestamp | Auto-generated |
updatedAt | DateTime | Last update timestamp | Auto-updated |
Relations:
- None (independent model for official servers)
Unique Constraints:
ampInstanceId: Unique AMP instance identifier
Indexes:
gameType: Fast filtering by game typeisRunning: Fast filtering of running serverslastSync: Fast identification of stale data
Game Types:
minecraft: Minecraft serversrust: Rust serversark: ARK: Survival Evolved serversvalheim: Valheim serverscsgo: Counter-Strike serversterraria: Terraria servers
Sync Behavior:
- Polled periodically via AMP API (configurable interval)
- Status updated automatically by background service
- Admin users can trigger manual sync via web panel
Relationships
Entity Relationship Diagram
Guild (1) ──┬─→ (N) Game
├─→ (N) ServerReview
├─→ (N) ServerMember
└─→ (N) GameServer
User (1) ──┬─→ (N) Session
├─→ (N) Announcement
├─→ (N) ServerReview
├─→ (N) Notification
├─→ (1) UserRank
├─→ (N) Permission
├─→ (N) ServerMember
├─→ (N) ForumThread
├─→ (N) ForumPost
├─→ (N) ForumPostReaction
├─→ (N) GameServer
└─→ (N) LiveChatMessage
ForumCategory (1) ──┬─→ (N) ForumThread
└─→ (N) ForumCategoryPermission
ForumThread (1) ──→ (N) ForumPost
ForumPost (1) ──→ (N) ForumPostReaction
GameServer (1) ──→ (N) GameServerStatus
Guild (N) ←─── (N) User (via ServerReview, ServerMember)
Cascade Behaviors
ON DELETE CASCADE:
-
When
Guildis deleted:- All related
Gamerecords deleted - All related
ServerReviewrecords deleted - All related
ServerMemberrecords deleted - All related
GameServerrecords deleted
- All related
-
When
Useris deleted:- All related
Sessionrecords deleted - All related
ServerReviewrecords deleted - All related
Notificationrecords deleted - All related
UserRankrecords deleted - All related
Permissionrecords deleted - All related
ServerMemberrecords deleted - All related
ForumThreadrecords deleted - All related
ForumPostrecords deleted - All related
ForumPostReactionrecords deleted - Related
GameServerrecords preserved (submitter reference) - All related
LiveChatMessagerecords deleted
- All related
-
When
ForumCategoryis deleted:- All related
ForumThreadrecords deleted (and their posts) - All related
ForumCategoryPermissionrecords deleted
- All related
-
When
ForumThreadis deleted:- All related
ForumPostrecords deleted (and their reactions)
- All related
-
When
ForumPostis deleted:- All related
ForumPostReactionrecords deleted
- All related
-
When
GameServeris deleted:- All related
GameServerStatusrecords deleted
- All related
Preserved Relations:
Announcement: Not cascaded (preserved even if author is deleted)GameServer.submittedBy: Not cascaded (submitter reference preserved)
Indexes and Performance
Critical Indexes
-
Guild:
isOfficial: Fast filtering of official serversisApproved: Fast filtering of approved serversisFeatured: Fast filtering of featured servers
-
Game:
guildId: Fast lookup of games by serverarchived: Fast filtering of active games
-
User:
role: Fast filtering of adminscreatedAt: Fast sorting by join date
-
Session:
userId: Fast lookup of user sessionsexpiresAt: Fast cleanup of expired sessions
-
Announcement:
(published, createdAt): Fast queries for published announcements
-
Notification:
(userId, read): Fast queries for unread notificationscreatedAt: Fast sorting by date
Query Optimization Tips
Efficient Queries:
// ✅ Good: Uses index on published + createdAt
const announcements = await prisma.announcement.findMany({
where: { published: true },
orderBy: { createdAt: 'desc' },
take: 10
});
// ✅ Good: Uses index on userId + read
const unreadCount = await prisma.notification.count({
where: { userId: userId, read: false }
});
// ❌ Bad: No index on username + name combination
const result = await prisma.user.findMany({
where: { username: { contains: 'test' } }
});
Migrations
Migration History
All migrations are stored in prisma/migrations/.
View Migration Status:
npx prisma migrate status
Create New Migration:
# Development
npm run db:migrate
# Production
npm run db:migrate:prod
Common Migration Tasks
Add New Field:
- Update
prisma/schema.prisma:
model User {
id String @id
email String? // New field
...
}
- Create and apply migration:
npm run db:migrate
Add Index:
- Update schema:
model User {
...
@@index([email])
}
- Apply migration:
npm run db:migrate
Database Maintenance
Cleanup Tasks
Expired Sessions:
DELETE FROM "Session" WHERE "expiresAt" < NOW();
Run this periodically (e.g., daily cron job).
Archived Games:
-- View archived games older than 6 months
SELECT * FROM "Game"
WHERE archived = true
AND "archivedAt" < NOW() - INTERVAL '6 months';
-- Permanently delete old archived games (use with caution!)
DELETE FROM "Game"
WHERE archived = true
AND "archivedAt" < NOW() - INTERVAL '6 months';
Old Notifications:
-- Delete read notifications older than 30 days
DELETE FROM "Notification"
WHERE read = true
AND "createdAt" < NOW() - INTERVAL '30 days';
Database Statistics
Table Sizes (PostgreSQL):
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
Row Counts:
SELECT
'User' as table_name, COUNT(*) as rows FROM "User"
UNION ALL
SELECT 'Guild', COUNT(*) FROM "Guild"
UNION ALL
SELECT 'Game', COUNT(*) FROM "Game"
UNION ALL
SELECT 'Session', COUNT(*) FROM "Session"
UNION ALL
SELECT 'Announcement', COUNT(*) FROM "Announcement"
UNION ALL
SELECT 'Notification', COUNT(*) FROM "Notification";
Schema Evolution
Adding New Features
When adding new features, consider:
- Backward Compatibility: Can existing data be migrated?
- Default Values: Use sensible defaults for new fields
- Indexes: Add indexes for frequently queried fields
- Relations: Use appropriate cascade behaviors
Example: Adding Server Tags
model ServerTag {
id String @id @default(cuid())
name String @unique
createdAt DateTime @default(now())
servers ServerTagRelation[]
}
model ServerTagRelation {
id String @id @default(cuid())
guildId String
guild Guild @relation(fields: [guildId], references: [id], onDelete: Cascade)
tagId String
tag ServerTag @relation(fields: [tagId], references: [id], onDelete: Cascade)
@@unique([guildId, tagId])
@@index([guildId])
@@index([tagId])
}
Troubleshooting
Common Issues
Issue: Prisma Client out of sync
Solution:
npm run db:generate
Issue: Migration failed
Solution:
# Mark migration as rolled back
npx prisma migrate resolve --rolled-back <migration_name>
# Fix the issue and re-run migration
npm run db:migrate
Issue: Database locked (SQLite)
Solution:
- Stop all processes accessing the database
- Restart the application
Issue: Foreign key constraint violation
Solution:
- Ensure related records exist before creating relationships
- Use
onDelete: CascadeoronDelete: SetNullappropriately
Best Practices
- Always backup before migrations in production
- Test migrations in development first
- Use transactions for related operations
- Add indexes for frequently queried fields
- Use CUID for primary keys (secure, sortable, URL-safe)
- Soft delete important records (use
archivedflags) - Cascade deletes for dependent records
- Validate input before database operations
- Use Prisma Client instead of raw SQL when possible
- Monitor database size and clean up old data
Resources
New Models Summary
The following models were added in the major platform update:
Permission System:
UserRank: Hierarchical rank system (MEMBER, MODERATOR, ADMIN)Permission: Granular permission overridesServerMember: Per-server membership and roles
Forum System:
ForumCategory: Forum categories/sectionsForumThread: Discussion threadsForumPost: Thread repliesForumPostReaction: Emoji reactionsForumCategoryPermission: Per-category permission overrides
Game Server System:
GameServer: Community game server submissionsGameServerStatus: Historical status monitoring
Real-time Chat:
LiveChatMessage: Homepage live chat messagesLiveChatSettings: Discord webhook integration configuration (singleton)
Last Updated: February 21, 2026