Code Review: Simple and Flexible DataManager Module

Hello everyone,

I recently developed a DataManager Module for Roblox that is designed to load and save player data. It’s kept simple but is also flexible and reusable for different data stores (e.g., for Leaderstats or Items). It uses UpdateAsync to efficiently update player data.



Here is the ModuleScript: (80 Lines)

local DataStoreService = game:GetService("DataStoreService")
local httpService = game:GetService("HttpService")

local DataManager = {}


-- //: If plr isn't valid: false.
-- //: If player is valid: true.
local function checkPlayer(plr: Player)
	if not plr or not plr:IsA("Player") then
		warn("Invalid Player as argument, got: "..tostring(typeof(plr)))
		return false
	end
	return true
end


-- //: Function for Loading Data
DataManager.LoadData = function(plr: Player, dataStoreName: string)
	if not checkPlayer(plr) then return end -- Check if player argument is valid
	
	-- Get the DataStore and the key:
	local dataStore = DataStoreService:GetDataStore(dataStoreName)
	local key = plr.UserId
	
	-- Try to get the Data for the Player:
	local success, returnValue = pcall(function()
		return dataStore:GetAsync(key)
	end)
	
	if success then
		-- Check if loaded data is nil or not:
		if returnValue ~= nil then
			print(plr.Name .. " Successfully Loaded this Data: " .. httpService:JSONEncode(returnValue))
			return returnValue
		else
			warn("No Data was found for Player: " .. plr.Name .. " (Key: " .. plr.UserId .. ")")
			return nil
		end
		
	else
		warn(plr.Name..": Error while Loading Data:", returnValue)
		return nil
	end
end


-- //: Function for Changing the players Data to something new
DataManager.ChangeData = function(plr: Player, dataStoreName: string, indexToChange: any, newValue: any)
	if not checkPlayer(plr) then return end -- Check if player argument is valid

	-- Get the DataStore and the key:
	local dataStore = DataStoreService:GetDataStore(dataStoreName)
	local key = plr.UserId
	
	-- If the key is empty, just create new table and assign value
	-- If key is not empty, just add the value to the table
	local success, err = pcall(function()
		
		dataStore:UpdateAsync(key, function(data)
			if data ~= nil then
				data[indexToChange] = newValue
			else
				data = { [indexToChange] = newValue }
			end
			return data -- Save the Changes
		end)
		
	end)
	
	if success then
		print("Successfully Changed Data for: "..plr.Name.." (Key: "..key..")")
	else
		warn("Error while Chaninging Data for: "..plr.Name.." (Key: "..key.."):", err)
	end
end



return DataManager

The module provides two main functions:

  • LoadData: Loads the player’s saved data from the specified DataStore.

  • ChangeData: Changes specific values in the key of the player.



Example usage with Leaderstats, ServerScript: (29 Lines)

local dataManager = require(game:GetService("ServerScriptService").Modules.DataManager)


game.Players.PlayerAdded:Connect(function(plr)
	
	-- Leaderstats:
	local leaderstats = Instance.new("Folder", plr)
	leaderstats.Name = "leaderstats"
	
	local money = Instance.new("NumberValue", leaderstats)
	money.Name = "Money"
	
	local level = Instance.new("NumberValue", leaderstats)
	level.Name = "Level"
	
	
	-- Load Data:
	local loadedData = dataManager.LoadData(plr, "PlayerData")
	if not loadedData then warn("No Data") return end
	
	plr.leaderstats.Money.Value = loadedData["Money"]
	plr.leaderstats.Level.Value = loadedData["Level"]
end)


game.Players.PlayerRemoving:Connect(function(plr)
	dataManager.ChangeData(plr, "PlayerData", "Money", plr.leaderstats.Money.Value)
	dataManager.ChangeData(plr, "PlayerData", "Level", plr.leaderstats.Level.Value)
end)

I tested it and it works


I’m looking for feedback and suggestions for improvement, especially regarding efficiency and best practices. Are there any optimizations I might have missed? Or is there something I should pay more attention to?

Thanks for your time and help!