Understanding these core concepts will help you effectively use gmsv_mongo in your Garry's Mod server.
gmsv_mongo follows MongoDB's standard client-server architecture:
┌─────────────────┐
│ GMod Server │
│ (Lua Code) │
└────────┬────────┘
│ require("mongo")
↓
┌─────────────────┐
│ gmsv_mongo │
│ (Rust Module) │
└────────┬────────┘
│ TCP/IP
↓
┌─────────────────┐
│ MongoDB Server │
│ (Database) │
└─────────────────┘
Data in gmsv_mongo is organized in a three-level hierarchy:
Client → Database → Collection → Document
Each level serves a specific purpose in organizing and managing your data.
The Client is your connection to the MongoDB server.
-- Create a client
local client = MongoDB.Client("mongodb://localhost:27017")
Key Points:
Best Practice:
-- Create ONE global client for your addon
if not DB then
DB = {}
DB.client = MongoDB.Client("mongodb://localhost:27017")
end
A Database is a container for collections. Think of it as a namespace for your data.
-- Get a database
local db = client:Database("my_gameserver")
Key Points:
darkrp_main, ttt_stats, sandbox_buildsExample Structure:
MongoDB Server
├── darkrp_main ← Database for DarkRP
│ ├── players
│ ├── jobs
│ └── transactions
├── ttt_statistics ← Database for TTT
│ ├── rounds
│ ├── player_stats
│ └── detective_logs
└── admin_tools ← Database for admin system
├── bans
├── warnings
└── logs
A Collection is a group of related documents. Similar to a table in SQL databases, but schema-less.
-- Get a collection
local players = db:Collection("players")
Key Points:
players, items, transactionsNaming Conventions:
-- Good collection names
local players = db:Collection("players")
local inventory_items = db:Collection("inventory_items")
local ban_records = db:Collection("ban_records")
-- Avoid
local data = db:Collection("data") -- Too generic
local Player = db:Collection("Player") -- Use lowercase, plural
A Document is a single record in a collection. It's a Lua table that gets converted to BSON.
-- A document
local playerDoc = {
steamid = "STEAM_0:1:12345",
name = "Player1",
level = 5,
inventory = {
{ item = "weapon_pistol", count = 1 },
{ item = "ammo_9mm", count = 50 }
},
stats = {
kills = 150,
deaths = 75,
playtime = 3600
}
}
Key Points:
_id fieldThe connection string tells gmsv_mongo where and how to connect:
-- Basic format
"mongodb://host:port"
-- With authentication
"mongodb://username:password@host:port"
-- With database
"mongodb://username:password@host:port/database"
-- With options
"mongodb://host:port/?option1=value1&option2=value2"
Examples:
-- Local development
local client = MongoDB.Client("mongodb://localhost:27017")
-- Production with auth
local client = MongoDB.Client("mongodb://admin:SecurePass123@db.example.com:27017")
-- With SSL
local client = MongoDB.Client("mongodb://user:pass@host:27017/?ssl=true")
-- MongoDB Atlas (cloud)
local client = MongoDB.Client("mongodb+srv://user:pass@cluster.mongodb.net/mydb")
For advanced configuration, use ClientWithOptions:
local client = MongoDB.ClientWithOptions("mongodb://localhost:27017", {
app_name = "MyGModServer", -- Identifies your app in MongoDB logs
max_pool_size = 50, -- Maximum connections in pool
min_pool_size = 5, -- Minimum connections to maintain
retry_writes = true, -- Automatically retry failed writes
retry_reads = true, -- Automatically retry failed reads
server_selection_timeout = 30, -- Seconds to wait for server selection
connect_timeout = 10 -- Seconds to wait for connection
})
gmsv_mongo automatically converts between Lua and BSON types:
| Lua Type | BSON Type | Example |
|---|---|---|
number (int) | Int32/Int64 | level = 5 |
number (float) | Double | rating = 4.5 |
string | String | name = "Player1" |
boolean | Boolean | active = true |
table (array) | Array | {1, 2, 3} |
table (object) | Document | {name = "test"} |
nil | Null | data = nil |
Special Types:
-- ObjectId (automatically created for _id)
local doc = players:FindOne({ steamid = "STEAM_0:1:12345" })
print(doc._id) -- "507f1f77bcf86cd799439011"
-- Timestamps (use os.time())
{
created_at = os.time(), -- Unix timestamp
updated_at = os.time()
}
-- Dates (use os.date())
{
date_string = os.date("%Y-%m-%d") -- "2026-01-19"
}
Every document has a unique _id field:
-- Insert returns the ID
local id = players:InsertOne({ name = "Player1" })
print("Document ID:", id) -- "507f1f77bcf86cd799439011"
-- Find by ID
local doc = players:FindOne({ _id = id })
-- Custom ID (not recommended unless necessary)
players:InsertOne({
_id = "custom_id_123", -- Use with caution
name = "Player1"
})
_id automaticallysteamid) for your queries_id for business logicAll gmsv_mongo operations are asynchronous under the hood:
-- Operations are non-blocking
local result = players:Find({ active = true })
-- The Lua code waits, but GMod server doesn't freeze
-- Other Lua threads continue executing
What this means:
Database operations can fail. Always handle errors:
-- Basic error checking
local result = players:FindOne({ steamid = steamid })
if not result then
-- Could mean not found OR error occurred
print("Player not found or error occurred")
end
-- Detailed error handling with pcall
local success, result = pcall(function()
return players:FindOne({ steamid = steamid })
end)
if success then
if result then
print("Player found!")
else
print("Player doesn't exist")
end
else
-- result contains error message
print("Database error:", result)
-- Log to file, notify admin, etc.
end
Use Indexes
Create indexes on frequently queried fields for 10-100x speed improvement.
Bulk Operations
Use InsertMany, UpdateMany instead of loops with single operations.
Limit Results
Always use limits on Find queries to avoid loading thousands of documents.
Project Fields
Only fetch the fields you need using aggregation pipelines.
-- 1. Module loads
require("mongo")
-- 2. Create client (happens once)
local client = MongoDB.Client("mongodb://localhost:27017")
-- 3. Get database reference (lightweight)
local db = client:Database("gameserver")
-- 4. Get collection reference (lightweight)
local players = db:Collection("players")
-- 5. Perform operations (async, pooled)
local result = players:Find({})
-- 6. Connection persists until server shutdown
-- No need to manually close connections