Overview
OwlData is OwlKnit's official persistence subsystem rewritten from scratch to be modern, asynchronous and decoupled. It acts as a unified interface between your game logic and whichever Roblox data-storage backend actually ends up saving the bytes.
Configuration
OwlData is fully self-contained: it's injected and initialized internally the moment Owl.Start() runs. Configuring persistence is just passing a Data table alongside the rest of your framework config.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Owl = require(ReplicatedStorage.OwlKnit.Owl)
Owl.Start({
Data = {
Backend = "ProfileStore", -- > // valid options: "ProfileStore" or "DataStore2"
AutoSave = true, -- > // enables/disables the automatic save loop
SaveInterval = 60, -- > // automatic save interval, in seconds
}
})What happens at startup:
- Environment check - OwlData immediately validates it's running on the server. A hard error is thrown if a client script tries to reach it.
- AutoSave loop - if
AutoSaveis on, OwlData usesHeartbeatcombined with aTroveto run regular save cycles without blocking the main thread or leaking memory over a long session. - Server shutdown - OwlData listens for the server closing to trigger an immediate, synchronous save of every active profile via its internal
:SaveAll()method so a sudden shutdown doesn't cost players their last few minutes of progress.
The OwlProfile API
Once a Load or Await promise resolves (see Profiles & Sessions), you get back an OwlProfile object a wrapper around the raw data table with high-level utility methods, so you're rarely reaching into profile.Data directly.
Reading
Profile:Get(key: string): any - reads a value with dot notation for descending into nested tables without risking a nil-indexing error:
local level = profile:Get("Stats.Level") -- > // equivalent to profile.Data.Stats.LevelProfile:GetAll(): table - returns a shallow copy of the entire profile's data. Useful for sending a full snapshot or bulk reads.
Writing
Profile:Set(key: string, value: any) - sets or replaces a value at a key, dot notation supported. Intermediate tables are created on the fly if they don't exist yet. Notifies any observers of that key instantly (see Profiles & Sessions for the observation system).
profile:Set("Stats.Experience", 1500)Profile:Update(key: string, fn: (oldValue: any) -> any) - modifies a value atomically through a callback, rather than a separate read-then-write:
profile:Update("Stats.Level", function(currentLevel)
return currentLevel + 1
end)Arithmetic and array utilities
Profile:Increment(key, amount?)- increments a numeric key byamount(default1). Throws if the key isn't a number.Profile:Decrement(key, amount?)- same, in reverse.Profile:Append(key, value)- pushes a value onto the end of an array-shaped table:profile:Append("Inventory.Badges", "Goblin_Hunter")Profile:Remove(key, value)- walks the array atkeyand removes every occurrence matchingvalue, re-indexing the table cleanly afterward.
Profile state
Profile:IsLoaded(): boolean- whether the profile is currently active and safe to modify.Profile:IsReleased(): boolean- whether the session has been closed (typically when the player leaves). Reading or writing a released profile logs a security warning rather than silently doing nothing.
Where to go next
- Architecture - the adapter system behind
Backend = "ProfileStore" | "DataStore2", and automatic fallback. - Profiles & Sessions -
Load/Get/Await, the automaticOwlOnProfileLoadedhook and protection against duplicate sessions.