๐ Schemas
Schemas define the structure, default values, and migrations for your data. This guide covers everything you need to know about schemas in Coppermind.
Creating a Schemaโ
local PlayerSchema = Coppermind.registerSchema({
name = "PlayerData",
dataTemplate = {
coins = 0,
gems = 0,
inventory = {},
},
migrations = {},
})
Each schema must have a unique name โ this is used as the DataStore name.
๐ Data Templatesโ
The dataTemplate defines the default structure for new data:
local dataTemplate = {
-- Primitives
coins = 0,
playerName = "",
isPremium = false,
-- Nested tables
stats = {
level = 1,
xp = 0,
playTime = 0,
},
-- Arrays
inventory = {},
completedQuests = {},
-- Complex nested structures
settings = {
audio = {
musicVolume = 1,
sfxVolume = 1,
},
display = {
showDamageNumbers = true,
},
},
}
๐ Data Reconciliationโ
When loading existing data, Coppermind automatically reconciles it with the template:
| Scenario | Behavior |
|---|---|
| Missing fields | Added with default values |
| Extra fields | Preserved (not removed) |
| Nested structures | Recursively reconciled |
-- If saved data is:
{ coins = 500 }
-- And template is:
{ coins = 0, gems = 0, stats = { level = 1 } }
-- โ
Loaded data becomes:
{ coins = 500, gems = 0, stats = { level = 1 } }
๐ Retrieving Schemasโ
Get a registered schema by name:
local schema = Coppermind.getSchema("PlayerData")
if schema then
print("Found schema:", schema.name)
end
๐ Type Safetyโ
For better type inference, define your schema with type annotations:
type PlayerData = {
coins: number,
gems: number,
inventory: { string },
stats: {
level: number,
xp: number,
},
}
local PlayerSchema = Coppermind.registerSchema({
name = "PlayerData",
dataTemplate = {
coins = 0,
gems = 0,
inventory = {},
stats = {
level = 1,
xp = 0,
},
} :: PlayerData,
migrations = {},
})
Type annotations provide:
- Better autocomplete in your IDE
- Compile-time error checking
- Self-documenting code
โ Best Practicesโ
1. Use Descriptive Namesโ
-- โ
Good
local PlayerSchema = Coppermind.registerSchema({
name = "PlayerData_v1",
-- ...
})
-- โ Avoid
local schema = Coppermind.registerSchema({
name = "Data",
-- ...
})
2. Initialize All Fieldsโ
Always provide default values for every field โ never use nil.
-- โ
Good
dataTemplate = {
coins = 0,
lastLogin = 0,
settings = {
musicEnabled = true,
},
}
-- โ Avoid leaving fields undefined
dataTemplate = {
coins = nil, -- Don't do this
}
3. Keep Templates Flat When Possibleโ
Deeply nested structures are harder to work with:
-- โ
Prefer flat structures
dataTemplate = {
audioMusicVolume = 1,
audioSfxVolume = 1,
displayShowDamage = true,
}
-- โ ๏ธ Use nesting sparingly
dataTemplate = {
settings = {
audio = {
volumes = {
music = 1, -- Very deep!
},
},
},
}
4. Version Your Schema Namesโ
If you need to make breaking changes, version your schema:
-- Original
local PlayerSchemaV1 = Coppermind.registerSchema({
name = "PlayerData_v1",
-- ...
})
-- New version with breaking changes
local PlayerSchemaV2 = Coppermind.registerSchema({
name = "PlayerData_v2",
-- ...
})
Changing the schema name creates a new DataStore. Use migrations for non-breaking changes instead.