Transform Roblox Data Management with rSQL
Managing data in Roblox games can be frustrating and error-prone. rSQL revolutionizes this process by introducing SQL-like syntax to Roblox’s DataStoreService, ProfileService, and Datakeep. Say goodbye to repetitive, clunky code and hello to clean, efficient, and scalable data management.
What is rSQL?
rSQL is a lightweight module that changes how developers handle data in Roblox. With its SQL-like commands such as CREATE
, SELECT
, INSERT
, UPDATE
, DELETE
, ALTER
, DROP COLUMN
, and others, rSQL provides a unified interface for DataStoreService, ProfileService, and Datakeep, making your workflows seamless and powerful.
Traditional vs. rSQL: A Side-by-Side Comparison
Aspect | Traditional Approach | rSQL Approach |
---|---|---|
Data Storage | Raw tables with manual key-value handling. | Tables with SQL-like commands for ease. |
Code Complexity | Nested loops and conditionals for queries. | Simple SQL syntax reduces code clutter. |
Error Handling | Limited, no traceback; custom implementation. | Built-in detailed error messages with trace. |
Promise Support | Requires external libraries for promises. | Native promise support for async workflows. |
Multi-Service Support | Separate logic for DataStore, ProfileService. | Unified API for all systems. |
Performance | Difficult to optimize manually. | Optimized query handling with less boilerplate. |
Why rSQL?
SQL-Like Simplicity
Forget about clunky table manipulations and deeply nested code. With rSQL, write clean, intuitive commands like:
-- :query() only takes one argument, being a string.
sqlConnection:query("SELECT * FROM PlayerData WHERE id = '1234'")
Unified Workflow
Use a single, cohesive interface for DataStoreService, ProfileService, and Datakeep.
Asynchronous Made Easy
Handle complex operations effortlessly with promise-based workflows:
sqlConnection:query("INSERT INTO PlayerData (id, name) VALUES ('1', 'Alice')")
:andThen(function()
return sqlConnection:query("SELECT * FROM PlayerData WHERE id = '1'")
end)
:andThen(function(result)
print("Data fetched:", result)
end)
:catch(function(error)
warn("Error occurred:", error)
end)
How Does It Work?
Setting Up rSQL for DataStore
1st way:
local rSQL = require(path.to.rSQL)
local dataStore = game:GetService("DataStoreService"):GetDataStore("YOUR_DATASTORE")
local sqlConfig = {
allowCreate = true,
allowInsert = true,
allowSelect = true,
allowUpdate = true,
allowDelete = true,
allowDrop = true,
allowTruncate = true,
}
local sqlConnection = rSQL:connect(dataStore, sqlConfig):expect()
-- do things with sqlConnection using sqlConnection:query(QUERY_HERE)
2nd way:
local rSQL = require(path.to.rSQL)
local datastore = game:GetService("DataStoreService"):GetDataStore("YOUR_DATASTORE")
local sqlConfig = {
allowCreate = true,
allowInsert = true,
allowSelect = true,
allowUpdate = true,
allowDelete = true,
allowDrop = true,
allowTruncate = true,
}
rSQL:connect(datastore, sqlConfig)
:andThen(function(connection)
print("[Server] Connected to DataStore!")
-- Save the connection globally for future queries
_G.dbConnection = connection
end)
:catch(function(error)
warn("Failed to connect to DataStore" .. error)
end)
-- do things in this script or in others with _G.dbConnection using _G.dbConnection:query(QUERY_HERE)
Setting Up rSQL for ProfileService
local ProfileService = require(path_to_profileservice)
local profileTemplate = {}
local profiles = {}
local ProfileStore = ProfileService.GetProfileStore(
"PlayerData",
profileTemplate
)
-- do not put ProfileStore in :connectToProfileService()
local sqlConfig = {
allowCreate = true,
allowInsert = true,
allowSelect = true,
allowUpdate = true,
allowDelete = true,
allowDrop = true,
allowTruncate = true,
}
local sqlConnection = rSQL:connectToProfileService(profiles, sqlConfig):expect()
Setting Up rSQL for Datakeep
local DataKeep = require(path_to_datakeep)
local dataTemplate = {}
local loadedKeeps = {}
local store = DataKeep.GetStore("PlayerData", dataTemplate, {}):expect()
local sqlConfig = {
allowCreate = true,
allowInsert = true,
allowSelect = true,
allowUpdate = true,
allowDelete = true,
allowDrop = true,
allowTruncate = true,
}
local sqlConnection = rSQL:connectToDatakeep(loadedKeeps, sqlConfig):expect()
Examples: rSQL in Action
Promise-Based Workflow
Write elegant and structured queries with promises:
sqlConnection:query("CREATE TABLE PlayerData (id, name, score)")
:andThen(function()
return sqlConnection:query("INSERT INTO PlayerData (id, name, score) VALUES ('1', 'Alice', '500')")
end)
:andThen(function()
return sqlConnection:query("SELECT * FROM PlayerData WHERE id = '1'")
end)
:andThen(function(result)
print("Player data retrieved:", result)
end)
:catch(function(error)
warn("Error occurred:", error)
end)
Non-Promise Workflow
For developers who prefer a traditional pcall
method:
local success, errorMessage = pcall(function()
sqlConnection:query("CREATE TABLE PlayerData (id, name, score)"):expect()
sqlConnection:query("INSERT INTO PlayerData (id, name, score) VALUES ('1', 'Alice', '500')"):expect()
local results = sqlConnection:query("SELECT * FROM PlayerData WHERE id = '1'"):expect()
print("Player data retrieved:", results)
end)
if not success then
warn("Error occurred:", errorMessage)
end
Performance Highlights
Metric | Traditional | rSQL |
---|---|---|
Lines of Code | 15+ | 5 |
Error Debugging | Manual and limited traceback. | Built-in detailed tracebacks. |
Async Management | Requires manual callbacks. | Promises streamline operations. |
Key Features of rSQL
-
SQL-Like Simplicity: Use commands like
CREATE
,INSERT
,UPDATE
,SELECT
. - Multi-Service Support: Unified interface for DataStoreService, ProfileService, and Datakeep.
- Native Promises: Simplify async workflows.
-
Configurable Permissions: Limit commands (e.g., disallow
DROP
orTRUNCATE
). - Debug-Friendly: Built-in error handling with tracebacks.
-
New Features:
ALTER TABLE
,DROP COLUMN
, and more advanced SQL operations are now supported!
Quick Start (Player score datastore with rSQL)
local PlayerDatastore: DataStore = game:GetService("DataStoreService"):GetDataStore("PlayerDatastore")
local Promise = require(game.ReplicatedStorage.Packages.promise)
local rSQL = require(game.ReplicatedStorage.Packages.rSQL)
-- Connect to the datastore with configuration
local DB_CONNECTION = rSQL:connect(PlayerDatastore, {
allowInsert = true,
allowCreate = true,
allowOverwrite = false,
allowSelect = true,
allowUpdate = true,
}):expect()
-- Initialize the database
local function onInit()
return Promise.new(function(resolve, reject)
warn("[rSQL Server] Initializing Datastore")
local createSuccess, createError = DB_CONNECTION:query("CREATE TABLE Players (ID, Name, Score)"):expect()
if createSuccess then
resolve()
else
reject(createError)
end
end)
end
-- Handle player joining
local function onPlayerJoined(player: Player)
return Promise.new(function(resolve, reject)
warn("[rSQL Server] Player joined:", player.Name)
local playerId = player.UserId
local query = string.format("SELECT * FROM Players WHERE ID = %d", playerId)
local playerData = DB_CONNECTION:query(query):expect()
print(playerData)
if type(playerData) == "table" and #playerData > 0 then
player:SetAttribute("Score", playerData[1].Score)
resolve(playerData[1])
else
local insertQuery = string.format(
"INSERT INTO Players (ID, Name, Score) VALUES (%d, '%s', %d)",
playerId, player.Name, 0
)
local insertSuccess, insertError = DB_CONNECTION:query(insertQuery):expect()
if insertSuccess then
player:SetAttribute("Score", 0)
resolve()
else
reject(insertError)
end
end
end)
end
-- Handle player leaving
local function onPlayerLeaving(player: Player)
return Promise.new(function(resolve, reject)
local playerId = player.UserId
local playerScore = player:GetAttribute("Score")
if playerScore ~= nil then
-- Build and execute the query
local updateQuery = string.format(
"UPDATE Players SET Score = %d WHERE ID = %d",
playerScore, playerId
)
print("[DEBUG] Update Query:", updateQuery)
local updateSuccess, updateError = DB_CONNECTION:query(updateQuery):expect()
if updateSuccess then
warn(string.format("[rSQL Server] Saved data for player %s (Score: %d)", player.Name, playerScore))
resolve()
else
warn(string.format(
"[rSQL Server] Failed to save data for player %s: %s",
player.Name, tostring(updateError)
))
reject(updateError)
end
else
warn("[rSQL Server] No 'Score' attribute found for player:", player.Name)
reject("No score attribute")
end
end)
end
-- Initialize and hook up events
onInit():andThen(function()
warn("[rSQL Server] Successfully initialized datastore!")
game.Players.PlayerAdded:Connect(function(player)
onPlayerJoined(player):catch(function(err)
warn("[rSQL Server] Error during player join:", err)
end)
end)
for _, player in ipairs(game.Players:GetPlayers()) do
onPlayerJoined(player):catch(function(err)
warn("[rSQL Server] Error during player join:", err)
end)
end
game.Players.PlayerRemoving:Connect(function(player)
onPlayerLeaving(player):catch(function(err)
warn("[rSQL Server] Error during player leave:", err)
end)
end)
end):catch(function(err)
error("[rSQL Server] Failed to initialize datastore:", err)
end)
New SQL-Like Functions in rSQL
Here are examples for the newly added features like ALTER TABLE
, DROP COLUMN
, and other advanced SQL-like operations:
ALTER TABLE: Add a Column
Add a new column to an existing table:
sqlConnection:query("ALTER TABLE PlayerData ADD COLUMN Age INTEGER") -- Types (INTEGRER) are always misc, they are used to enhance code readability
:andThen(function()
print("Column 'Age' added successfully.")
end)
:catch(function(error)
warn("Error adding column:", error)
end)
ALTER TABLE: Drop a Column
Remove a column from an existing table:
sqlConnection:query("ALTER TABLE PlayerData DROP COLUMN Age")
:andThen(function()
print("Column 'Age' dropped successfully.")
end)
:catch(function(error)
warn("Error dropping column:", error)
end)
DROP TABLE
Delete an entire table:
sqlConnection:query("DROP TABLE PlayerData")
:andThen(function()
print("Table 'PlayerData' dropped successfully.")
end)
:catch(function(error)
warn("Error dropping table:", error)
end)
TRUNCATE TABLE
Clear all records from a table:
sqlConnection:query("TRUNCATE TABLE PlayerData")
:andThen(function()
print("Table 'PlayerData' truncated successfully.")
end)
:catch(function(error)
warn("Error truncating table:", error)
end)
Get Started with rSQL
- Download rSQL: From the Creator Market
- Follow Documentation: Read the Docs
- Simplify Your Workflow: Start managing data the SQL way.
Feedback & Support
Have questions or feedback?
Reach out via Twitter or open an issue on GitHub.