Skip to main content

ProfileDB

A class for managing player profiles. This class is used to store and retrieve player data. It can be used to store player data such as currency, inventory, and other player-specific data.

Key Features:

  • Automatically loads and saves player data to the data store
  • Automatically reconciles player data with a template
  • Supports saving player data to a web API if provided
  • Session locking to prevent multiple servers from saving the same player data at the same time
  • Supports saving player data in Studio
local Players = game:GetService("Players")
local ServerScriptService = game:GetService("ServerScriptService")

local ProfileDB = require(ServerScriptService.ProfileDB)

local ProfileDataTemplate = {
	coins = 100,
	inventory = {},
}

local profileDBSettings: ProfileDB.Settings = {
	mockDataStore = false,
	dataStoreName = "Profile",
	dataStoreVersion = 1,
	template = ProfileDataTemplate,
}

Players.PlayerAdded:Connect(function(player)
	local profile = ProfileDB.new(player.UserId, profileDBSettings)

	profile:Reconcile() -- Add any missing tables to the player's profile

	local profileData = profile:GetData()
	profileData.coins += 100 -- Add 100 coins to the player's profile

	profile:Save() -- Save the player's profile data
end)

Need to track updates to the player's profile data?

profile.Changed:Connect(function(profileData)
	print("Player data was updated", profileData.data.coins)
end)

Install with wally by adding the following to your wally.toml:

ProfileDB = "dig1t/profiledb@1.0.5"

Types

ProfileData

type ProfileData = {[string]any}

ProfileData contains the player's data

ProfileMetadata

interface ProfileMetadata {
dataProfileData--

The player's data.

creatednumber--

The time the profile was created.

last_seennumber--

The time the player was last seen.

sessionsnumber--

The number of times the player has joined the game.

sessionData{
lastUpdatenumber,
jobIdstring
}?--

Session data for the player.

}

ProfileMetadata contains everything that's saved to Roblox.

The data table will contain the player's data.

Template

type Template = ProfileData | (Player) → ProfileData

Template is the template for the profile data. It can be a table of default values or a function that returns a table of default values.

Properties

canSaveToDataStore

ProfileDB.canSaveToDataStore: boolean

Whether or not the game can save to the data store.

Functions

new

ProfileDB.new(
userIdnumber,--

The user ID of the player

settingsSettings?--

The settings for the profile

) → Profile--

The new ProfileDB instance

Types

interface Settings {
saveToDataStoreboolean--

If the mock data store should be used.

dataStoreNamestring--

The name of the data store.

dataStoreVersionnumber?--

The version of the data store.

apiURLstring?--

If provided, this URL will be used to send saved player data to an API.

saveInStudioboolean?--

If the data store should be used in Studio.

saveIntervalnumber--

The interval in seconds to save the player's data.

keysToIgnore{string}?--

The keys to ignore when saving the player's data.

templateProfileData | (Player?) → ProfileData--

The template for profile data.

}

Creates a new ProfileDB instance

A settings table can be provided to configure the ProfileDB instance.

GetTemplate

ProfileDB:GetTemplate() → ProfileData--

The template provided in the constructor or the result of the template function if the template is a function

Returns the template given in the constructor

GetLastVersion

ProfileDB:GetLastVersion() → (
boolean,
string?
)--

Whether or not the fetch was successful and the last version if successful

Fetches the last version from the version history data store

GetNewData

ProfileDB:GetNewData() → ProfileMetadata--

A new profile data object

Creates a new profile data object

Save

ProfileDB:Save(
releaseSessionboolean?--

Whether or not to release the session lock

) → boolean--

Whether or not the profile was saved

Returns whether or not the profile was saved

Reconcile

ProfileDB:Reconcile() → ()

Reconciles the profile data with the template provided in the constructor

Reset

ProfileDB:Reset() → ()

Resets the profile data to the template provided in the constructor

Get

ProfileDB:Get(
pathstring?--

The path to the value

) → any--

The value at the given path or nil if the value does not exist

Returns the value at the given path in the profile data

Not providing a path will return the entire profile data table

Set

ProfileDB:Set(
pathstring,--

The path to the value

valueany,--

The value to set

skipUpdateboolean?--

Whether or not to skip firing the Changed signal

) → boolean--

Whether or not the value was set

Sets the value at the given path in the profile data.

If nil is provided as the value, the key will be removed from the path.

If you want to insert a value into a table, you can use "++" as the last key in the path.

Example:

profile:Set("purchases.++", "dev_product_receipt_12345")

Need to store unique data in a table?

profile:Set("inventory.weapons.++", {
	uniqueWeaponId = "weapon_12345",
	skin = "RARE_SKIN_ID",
})

If the weapon were to be sold or removed, you can remove it by finding its index.

for index, weapon in profile:Get("inventory.weapons") do
	if weapon.uniqueWeaponId == "weapon_12345" then
		profile:Set("inventory.weapons.--", index)
		break
	end
end

SetMultiple

ProfileDB:SetMultiple(
data{[string]any}--

The data paths to set

) → {string}--

The keys that were updated

Sets multiple values in the profile data.

Unset

ProfileDB:Unset(
pathstring--

The path to the value

) → boolean--

Whether or not the value was unset

Unsets the value at the given path in the profile data.

Alias for profile:Set(path, nil).

UnsetMultiple

ProfileDB:UnsetMultiple(
data{string}--

The data paths to unset

) → {string}--

The keys that were removed

Unsets multiple values in the profile data.

Insert

ProfileDB:Insert(
pathstring,--

The path to the table

valueany,--

The value to insert

skipUpdateboolean?--

Whether or not to skip firing the Changed signal

) → boolean--

Whether or not the value was inserted

Inserts a number indexed value into a table at the given path.

Example

print(profile.metadata.data.inventory) -- { "item_12345", "item_67890" }

profile:Insert("inventory", "item_54321")

print(profile.metadata.data.inventory) -- { "item_12345", "item_67890", "item_54321" }

Need to insert it into a nested table?

print(profile.metadata.data.inventory.weapons) -- { "weapon_12345", "weapon_67890" }

profile:Insert("inventory.weapons", "weapon_54321")

print(profile.metadata.data.inventory) -- { "weapon_12345", "weapon_67890", "weapon_54321" }

InsertMultiple

ProfileDB:InsertMultiple(
pathstring,--

The path to the table

data{any}--

The values to insert

) → {string}--

The keys that were updated

Inserts multiple values into a table at the given path.

RemoveValue

ProfileDB:RemoveValue(
pathstring,--

The path to the table

valueany,--

The value to remove

skipUpdateboolean?--

Whether or not to skip firing the Changed signal

) → boolean--

Whether or not the value was removed

This method will remove a value from a table.

Example

print(profile.metadata.data) -- { inventory = { "item_12345", "item_67890" } }

profile:RemoveValue("inventory", "item_12345")

print(profile.metadata.data) -- { inventory = { "item_67890" } }

RemoveMultiple

ProfileDB:RemoveMultiple(
pathstring,--

The path to the table

data{[string]any}--

The path and values to remove

) → {string}--

The keys that were removed

Removes multiple values from a table at the given path.

Increment

ProfileDB:Increment(
pathstring,--

The path to the value

valuenumber--

The value to increment by

) → boolean--

Whether or not the value was incremented

Increments the value at the given path in the profile data.

SetData

ProfileDB:SetData(
callback(ProfileMetadata) → ()--

The callback to update the profile data

) → boolean--

Whether or not the data was updated

Updates the profile data using a callback function.

This method can be dangerous and destructive if used incorrectly. For example yielding in the callback function can cause data loss.

Example

profile:SetData(function(data: ProfileDB.ProfileMetadata)
	data.coins = 100
end)

Destroy

Cleanup
ProfileDB:Destroy() → ()

Destroys the profile and disconnects all events.

Show raw api
{
    "functions": [
        {
            "name": "new",
            "desc": "Creates a new ProfileDB instance\n\nA settings table can be provided to configure the ProfileDB instance.",
            "params": [
                {
                    "name": "userId",
                    "desc": "The user ID of the player",
                    "lua_type": "number"
                },
                {
                    "name": "settings",
                    "desc": "The settings for the profile",
                    "lua_type": "Settings?"
                }
            ],
            "returns": [
                {
                    "desc": "The new ProfileDB instance",
                    "lua_type": "Profile"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 229,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "GetTemplate",
            "desc": "Returns the template given in the constructor",
            "params": [],
            "returns": [
                {
                    "desc": "The template provided in the constructor or the result of the template function if the template is a function",
                    "lua_type": "ProfileData"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 366,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "GetLastVersion",
            "desc": "Fetches the last version from the version history data store",
            "params": [],
            "returns": [
                {
                    "desc": "Whether or not the fetch was successful and the last version if successful",
                    "lua_type": "(boolean, string?)"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 439,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "GetNewData",
            "desc": "Creates a new profile data object",
            "params": [],
            "returns": [
                {
                    "desc": "A new profile data object",
                    "lua_type": "ProfileMetadata"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 459,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "LoadData",
            "desc": "Fetches the latest player metadata from the data store",
            "params": [],
            "returns": [
                {
                    "desc": "The player's metadata or a new profile data object if the player is new",
                    "lua_type": "ProfileMetadata"
                }
            ],
            "function_type": "method",
            "private": true,
            "source": {
                "line": 478,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "Save",
            "desc": "Returns whether or not the profile was saved",
            "params": [
                {
                    "name": "releaseSession",
                    "desc": "Whether or not to release the session lock",
                    "lua_type": "boolean?"
                }
            ],
            "returns": [
                {
                    "desc": "Whether or not the profile was saved",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 589,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "Reconcile",
            "desc": "Reconciles the profile data with the template provided in the constructor",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 664,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "Reset",
            "desc": "Resets the profile data to the template provided in the constructor",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 695,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "Get",
            "desc": "Returns the value at the given path in the profile data\n\nNot providing a path will return the entire profile data table",
            "params": [
                {
                    "name": "path",
                    "desc": "The path to the value",
                    "lua_type": "string?"
                }
            ],
            "returns": [
                {
                    "desc": "The value at the given path or nil if the value does not exist",
                    "lua_type": "any"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 710,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "Set",
            "desc": "Sets the value at the given path in the profile data.\n\nIf nil is provided as the value, the key will be removed from the path.\n\nIf you want to insert a value into a table, you can use \"++\" as the last key in the path.\n\nExample:\n```lua\nprofile:Set(\"purchases.++\", \"dev_product_receipt_12345\")\n```\n\nNeed to store unique data in a table?\n```lua\nprofile:Set(\"inventory.weapons.++\", {\n\tuniqueWeaponId = \"weapon_12345\",\n\tskin = \"RARE_SKIN_ID\",\n})\n```\n\nIf the weapon were to be sold or removed, you can remove it by finding its index.\n```lua\nfor index, weapon in profile:Get(\"inventory.weapons\") do\n\tif weapon.uniqueWeaponId == \"weapon_12345\" then\n\t\tprofile:Set(\"inventory.weapons.--\", index)\n\t\tbreak\n\tend\nend\n```",
            "params": [
                {
                    "name": "path",
                    "desc": "The path to the value",
                    "lua_type": "string"
                },
                {
                    "name": "value",
                    "desc": "The value to set",
                    "lua_type": "any"
                },
                {
                    "name": "skipUpdate",
                    "desc": "Whether or not to skip firing the Changed signal",
                    "lua_type": "boolean?"
                }
            ],
            "returns": [
                {
                    "desc": "Whether or not the value was set",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 772,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "SetMultiple",
            "desc": "Sets multiple values in the profile data.",
            "params": [
                {
                    "name": "data",
                    "desc": "The data paths to set",
                    "lua_type": "{ [string]: any }"
                }
            ],
            "returns": [
                {
                    "desc": "The keys that were updated",
                    "lua_type": "{ string }"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 844,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "Unset",
            "desc": "Unsets the value at the given path in the profile data.\n\nAlias for `profile:Set(path, nil)`.",
            "params": [
                {
                    "name": "path",
                    "desc": "The path to the value",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "Whether or not the value was unset",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 875,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "UnsetMultiple",
            "desc": "Unsets multiple values in the profile data.",
            "params": [
                {
                    "name": "data",
                    "desc": "The data paths to unset",
                    "lua_type": "{ string }"
                }
            ],
            "returns": [
                {
                    "desc": "The keys that were removed",
                    "lua_type": "{ string }"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 887,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "Insert",
            "desc": "Inserts a number indexed value into a table at the given path.\n\nExample\n```lua\nprint(profile.metadata.data.inventory) -- { \"item_12345\", \"item_67890\" }\n\nprofile:Insert(\"inventory\", \"item_54321\")\n\nprint(profile.metadata.data.inventory) -- { \"item_12345\", \"item_67890\", \"item_54321\" }\n```\n\nNeed to insert it into a nested table?\n```lua\nprint(profile.metadata.data.inventory.weapons) -- { \"weapon_12345\", \"weapon_67890\" }\n\nprofile:Insert(\"inventory.weapons\", \"weapon_54321\")\n\nprint(profile.metadata.data.inventory) -- { \"weapon_12345\", \"weapon_67890\", \"weapon_54321\" }\n```",
            "params": [
                {
                    "name": "path",
                    "desc": "The path to the table",
                    "lua_type": "string"
                },
                {
                    "name": "value",
                    "desc": "The value to insert",
                    "lua_type": "any"
                },
                {
                    "name": "skipUpdate",
                    "desc": "Whether or not to skip firing the Changed signal",
                    "lua_type": "boolean?"
                }
            ],
            "returns": [
                {
                    "desc": "Whether or not the value was inserted",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 936,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "InsertMultiple",
            "desc": "Inserts multiple values into a table at the given path.",
            "params": [
                {
                    "name": "path",
                    "desc": "The path to the table",
                    "lua_type": "string"
                },
                {
                    "name": "data",
                    "desc": "The values to insert",
                    "lua_type": "{ any }"
                }
            ],
            "returns": [
                {
                    "desc": "The keys that were updated",
                    "lua_type": "{ string }"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 994,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "RemoveValue",
            "desc": "This method will remove a value from a table.\n\nExample\n```lua\nprint(profile.metadata.data) -- { inventory = { \"item_12345\", \"item_67890\" } }\n\nprofile:RemoveValue(\"inventory\", \"item_12345\")\n\nprint(profile.metadata.data) -- { inventory = { \"item_67890\" } }\n```",
            "params": [
                {
                    "name": "path",
                    "desc": "The path to the table",
                    "lua_type": "string"
                },
                {
                    "name": "value",
                    "desc": "The value to remove",
                    "lua_type": "any"
                },
                {
                    "name": "skipUpdate",
                    "desc": "Whether or not to skip firing the Changed signal",
                    "lua_type": "boolean?"
                }
            ],
            "returns": [
                {
                    "desc": "Whether or not the value was removed",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 1034,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "RemoveMultiple",
            "desc": "Removes multiple values from a table at the given path.",
            "params": [
                {
                    "name": "path",
                    "desc": "The path to the table",
                    "lua_type": "string"
                },
                {
                    "name": "data",
                    "desc": "The path and values to remove",
                    "lua_type": "{ [string]: any }"
                }
            ],
            "returns": [
                {
                    "desc": "The keys that were removed",
                    "lua_type": "{ string }"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 1098,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "Increment",
            "desc": "Increments the value at the given path in the profile data.",
            "params": [
                {
                    "name": "path",
                    "desc": "The path to the value",
                    "lua_type": "string"
                },
                {
                    "name": "value",
                    "desc": "The value to increment by",
                    "lua_type": "number"
                }
            ],
            "returns": [
                {
                    "desc": "Whether or not the value was incremented",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 1128,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "SetData",
            "desc": "Updates the profile data using a callback function.\n\nThis method can be dangerous and destructive if used incorrectly.\nFor example yielding in the callback function can cause data loss.\n\nExample\n```lua\nprofile:SetData(function(data: ProfileDB.ProfileMetadata)\n\tdata.coins = 100\nend)\n```",
            "params": [
                {
                    "name": "callback",
                    "desc": "The callback to update the profile data",
                    "lua_type": "(ProfileMetadata) -> ()"
                }
            ],
            "returns": [
                {
                    "desc": "Whether or not the data was updated",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 1167,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "Destroy",
            "desc": "Destroys the profile and disconnects all events.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "tags": [
                "Cleanup"
            ],
            "source": {
                "line": 1186,
                "path": "src/ProfileDB/init.luau"
            }
        }
    ],
    "properties": [
        {
            "name": "canSaveToDataStore",
            "desc": "Whether or not the game can save to the data store.",
            "lua_type": "boolean",
            "source": {
                "line": 175,
                "path": "src/ProfileDB/init.luau"
            }
        }
    ],
    "types": [
        {
            "name": "ProfileData",
            "desc": "ProfileData contains the player's data",
            "lua_type": "{ [string]: any }",
            "source": {
                "line": 85,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "ProfileMetadata",
            "desc": "ProfileMetadata contains everything that's saved to Roblox.\n\nThe `data` table will contain the player's data.",
            "fields": [
                {
                    "name": "data",
                    "lua_type": "ProfileData",
                    "desc": "The player's data."
                },
                {
                    "name": "created",
                    "lua_type": "number",
                    "desc": "The time the profile was created."
                },
                {
                    "name": "last_seen",
                    "lua_type": "number",
                    "desc": "The time the player was last seen."
                },
                {
                    "name": "sessions",
                    "lua_type": "number",
                    "desc": "The number of times the player has joined the game."
                },
                {
                    "name": "sessionData",
                    "lua_type": "{ lastUpdate: number, jobId: string }?",
                    "desc": "Session data for the player."
                }
            ],
            "source": {
                "line": 101,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "Template",
            "desc": "Template is the template for the profile data.\nIt can be a table of default values or a function that returns a table of default values.",
            "lua_type": "ProfileData | (Player) -> ProfileData",
            "source": {
                "line": 121,
                "path": "src/ProfileDB/init.luau"
            }
        },
        {
            "name": "Settings",
            "desc": "",
            "fields": [
                {
                    "name": "saveToDataStore",
                    "lua_type": "boolean",
                    "desc": "If the mock data store should be used."
                },
                {
                    "name": "dataStoreName",
                    "lua_type": "string",
                    "desc": "The name of the data store."
                },
                {
                    "name": "dataStoreVersion",
                    "lua_type": "number?",
                    "desc": "The version of the data store."
                },
                {
                    "name": "apiURL",
                    "lua_type": "string?",
                    "desc": "If provided, this URL will be used to send saved player data to an API."
                },
                {
                    "name": "saveInStudio",
                    "lua_type": "boolean?",
                    "desc": "If the data store should be used in Studio."
                },
                {
                    "name": "saveInterval",
                    "lua_type": "number",
                    "desc": "The interval in seconds to save the player's data."
                },
                {
                    "name": "keysToIgnore",
                    "lua_type": "{ string }?",
                    "desc": "The keys to ignore when saving the player's data."
                },
                {
                    "name": "template",
                    "lua_type": "ProfileData | (Player?) -> ProfileData",
                    "desc": "The template for profile data."
                }
            ],
            "source": {
                "line": 137,
                "path": "src/ProfileDB/init.luau"
            }
        }
    ],
    "name": "ProfileDB",
    "desc": "A class for managing player profiles.\nThis class is used to store and retrieve player data.\nIt can be used to store player data such as currency, inventory, and other player-specific data.\n\nKey Features:\n- Automatically loads and saves player data to the data store\n- Automatically reconciles player data with a template\n- Supports saving player data to a web API if provided\n- Session locking to prevent multiple servers from saving the same player data at the same time\n- Supports saving player data in Studio\n\n```lua\nlocal Players = game:GetService(\"Players\")\nlocal ServerScriptService = game:GetService(\"ServerScriptService\")\n\nlocal ProfileDB = require(ServerScriptService.ProfileDB)\n\nlocal ProfileDataTemplate = {\n\tcoins = 100,\n\tinventory = {},\n}\n\nlocal profileDBSettings: ProfileDB.Settings = {\n\tmockDataStore = false,\n\tdataStoreName = \"Profile\",\n\tdataStoreVersion = 1,\n\ttemplate = ProfileDataTemplate,\n}\n\nPlayers.PlayerAdded:Connect(function(player)\n\tlocal profile = ProfileDB.new(player.UserId, profileDBSettings)\n\n\tprofile:Reconcile() -- Add any missing tables to the player's profile\n\n\tlocal profileData = profile:GetData()\n\tprofileData.coins += 100 -- Add 100 coins to the player's profile\n\n\tprofile:Save() -- Save the player's profile data\nend)\n```\n\nNeed to track updates to the player's profile data?\n```lua\nprofile.Changed:Connect(function(profileData)\n\tprint(\"Player data was updated\", profileData.data.coins)\nend)\n```\n\nInstall with wally by adding the following to your `wally.toml`:\n```toml\nProfileDB = \"dig1t/profiledb@1.0.5\"\n```",
    "source": {
        "line": 57,
        "path": "src/ProfileDB/init.luau"
    }
}