Menu
 

Roblox GSB Quickstart — External Backend in 5 Minutes

Roblox GSB Quickstart

This guide walks you from zero to a working Roblox server script that saves player data, submits leaderboard scores, and manages virtual economy — all through Supercraft GSB, an external backend with no DataStore rate limits and built-in cross-experience support.

Everything in this guide runs from a server-side Script. HttpService is the only Roblox API you need.

What you'll have at the end: a Roblox server script that verifies players, reads and writes persistent data with optimistic locking, submits scores to a live leaderboard, and credits in-game currency — all backed by an external database your team can operate from a dashboard.

Prerequisites

  • A Roblox experience with Allow HTTP Requests enabled (Game Settings → Security).
  • A GSB account and a project with at least one environment. Create one here.
  • A Server Token from your project's Tokens tab. This is what your Roblox server uses to authenticate with GSB. Keep it secret — never put it in a LocalScript.

Step 1 — Enable HttpService

In Roblox Studio, open Game Settings → Security and turn on Allow HTTP Requests. Without this, all external HTTP calls will silently fail.

Security note: HTTP requests made through HttpService happen on your Roblox game server, not on players' machines. The server token never leaves your server scripts.

Step 2 — Install the GSB Module

Download GSB.lua from the GSB SDK page and add it to your experience:

  1. In Studio, insert a ModuleScript inside ServerScriptService.
  2. Rename it to GSB.
  3. Paste the full contents of GSB.lua into the script body.

The module is self-contained — no external packages, no toolchain. One paste and it is ready.

Step 3 — Initialize the Client

In any server-side Script (not a LocalScript), require the module and call GSB.init with your project credentials:

-- ServerScriptService/GameServer (Script)

local GSB = require(game.ServerScriptService.GSB)

local gsb = GSB.init(
    "YOUR_PROJECT_ID",    -- e.g. "proj_01abc..."
    "YOUR_SERVER_TOKEN",  -- e.g. "gsb_st_..."
    "YOUR_ENVIRONMENT_ID" -- e.g. "env_01abc..."  (use your "production" env)
)

You can find all three values in your project dashboard under Settings → Environments and Settings → Tokens.

Step 4 — Verify Players on Join

When a player joins, call gsb:VerifyPlayer with their Roblox UserId. This creates a GSB player record the first time, or returns the existing one on subsequent joins. The returned player_id is what you use for all subsequent API calls.

local Players = game:GetService("Players")

-- Maps Roblox UserId → GSB player_id for this session
local playerIds = {}

Players.PlayerAdded:Connect(function(player)
    local ok, result = pcall(function()
        return gsb:VerifyPlayer(player.UserId)
    end)

    if ok then
        playerIds[player.UserId] = result.player_id
        print("[GSB] Verified " .. player.Name .. " → " .. result.player_id)
    else
        warn("[GSB] Verify failed for " .. player.Name .. ": " .. tostring(result))
    end
end)

Players.PlayerRemoving:Connect(function(player)
    playerIds[player.UserId] = nil
end)

Step 5 — Save and Load Player Data

GSB exposes a DataStore-compatible API so the pattern looks familiar. Under the hood it stores versioned JSON documents without the 6-second cooldown or per-key rate limits of Roblox DataStores.

Basic get / set

-- Get a player's save data
local profileStore = gsb:GetDataStore("profile")

local function loadProfile(robloxUserId)
    local playerId = playerIds[robloxUserId]
    if not playerId then return end

    local data, version = profileStore:GetAsync(playerId)
    -- data is a Lua table (or nil if no save exists yet)
    return data, version
end

local function saveProfile(robloxUserId, data)
    local playerId = playerIds[robloxUserId]
    if not playerId then return end

    profileStore:SetAsync(playerId, data)
end

Safe updates with optimistic locking

For writes that must not overwrite concurrent changes — such as incrementing XP when multiple servers could write the same player — use UpdateAsync. It fetches the current version, passes the value to your transform function, and retries automatically if another server wrote first.

local function addXP(robloxUserId, amount)
    local playerId = playerIds[robloxUserId]
    if not playerId then return end

    profileStore:UpdateAsync(playerId, function(current)
        current = current or { xp = 0, level = 1 }
        current.xp = current.xp + amount
        -- level up every 1000 XP
        current.level = math.floor(current.xp / 1000) + 1
        return current
    end)
end

Comparison: DataStore vs GSB

Feature Roblox DataStore GSB
API shape GetAsync / SetAsync / UpdateAsync Same — drop-in compatible
Rate limits 60 + playerCount × 10 req/min per key Configurable per-project, 10–100× higher
Cross-experience No — scoped to one experience Yes — shared ProjectID across any experience
Dashboard / support tools Roblox Open Cloud only Built-in: search, view, edit, audit log
Versioned writes Version history (limited retention) ETag-based optimistic locking, always

Step 6 — Leaderboards

First, create a leaderboard in your project dashboard (Leaderboards → New). Give it a name and set the sort order (descending for high-score, ascending for fastest time). Then use the GSB client to submit and read scores:

-- Submit a score when a round ends
local function onRoundEnd(robloxUserId, finalScore)
    local playerId = playerIds[robloxUserId]
    if not playerId then return end

    gsb:SubmitScore("weekly-kills", playerId, finalScore, {
        -- optional metadata attached to this entry
        weapon = "shotgun",
        mode   = "tdm",
    })
end

-- Show the top 10 at game start (e.g. to populate a lobby board)
local function showLeaderboard()
    local entries = gsb:GetLeaderboardTop("weekly-kills", 10)
    for _, entry in ipairs(entries) do
        print(string.format("#%d  %s  %d", entry.rank, entry.player_id, entry.score))
    end
end

-- Show where a specific player ranks
local function showPlayerRank(robloxUserId)
    local playerId = playerIds[robloxUserId]
    local standing = gsb:GetPlayerStanding("weekly-kills", playerId)
    if standing then
        print("You are rank " .. standing.rank .. " with " .. standing.score .. " kills")
    end
end

Seasonal resets: configure a reset schedule (daily / weekly / monthly) in the dashboard. GSB archives the previous period automatically and starts a fresh board — no script changes needed.

Step 7 — Economy

GSB provides server-authoritative currency balances and item inventories. All adjustments are atomic — you can credit gold and add items in a single call without worrying about partial failures.

Read a player's economy

local function getEconomy(robloxUserId)
    local playerId = playerIds[robloxUserId]
    if not playerId then return end

    local economy = gsb:GetPlayerEconomy(playerId)

    -- economy.balances = list of { currency_id, currency_name, amount }
    for _, bal in ipairs(economy.balances or {}) do
        print(bal.currency_name .. ": " .. bal.amount)
    end

    -- economy.inventory = list of { item_id, item_name, quantity }
    for _, item in ipairs(economy.inventory or {}) do
        print(item.item_name .. " ×" .. item.quantity)
    end
end

Award currency and items on a kill

-- Currency IDs and item IDs come from your project dashboard
local GOLD_CURRENCY_ID  = "curr_gold_..."
local CRATE_ITEM_ID     = "item_crate_..."

local function onKillReward(robloxUserId)
    local playerId = playerIds[robloxUserId]
    if not playerId then return end

    gsb:AdjustEconomy(
        playerId,
        -- balance adjustments: {currency_id, amount}
        {{ currency_id = GOLD_CURRENCY_ID, amount = 50 }},
        -- inventory adjustments: {item_id, quantity}
        {{ item_id = CRATE_ITEM_ID, quantity = 1 }}
    )
end

Step 8 — Matchmaking (optional)

If you want to queue players across servers or experiences, GSB includes a matchmaking service. Typical usage: call JoinMatchmaking when a player clicks "Find Game", poll GetMatchStatus every few seconds, and start the match when all participants show status = "matched".

-- Queue a player
gsb:JoinMatchmaking(playerId, "ranked-2v2", "eu-west")

-- Poll until matched (run this in a separate coroutine or loop)
local function waitForMatch(playerId)
    for _ = 1, 30 do -- 30 × 2s = 60s timeout
        task.wait(2)
        local status = gsb:GetMatchStatus(playerId)
        if status and status.status == "matched" then
            return status.match
        end
    end
    gsb:LeaveMatchmaking(playerId) -- timed out — leave gracefully
    return nil
end

Putting It All Together

A minimal but complete server script for a session-based game:

-- ServerScriptService/GameServer

local GSB     = require(game.ServerScriptService.GSB)
local Players = game:GetService("Players")

local gsb = GSB.init("YOUR_PROJECT_ID", "YOUR_SERVER_TOKEN", "YOUR_ENVIRONMENT_ID")

local GOLD_ID = "curr_gold_..."
local playerIds = {}

Players.PlayerAdded:Connect(function(player)
    local ok, result = pcall(gsb.VerifyPlayer, gsb, player.UserId)
    if not ok then return end
    local pid = result.player_id
    playerIds[player.UserId] = pid

    -- Load profile
    local ds = gsb:GetDataStore("profile")
    local profile = ds:GetAsync(pid) or { xp = 0, level = 1, kills = 0 }

    -- Store in player attribute for easy access
    player:SetAttribute("gsb_pid",   pid)
    player:SetAttribute("xp",        profile.xp)
    player:SetAttribute("level",     profile.level)
    player:SetAttribute("kills",     profile.kills)
end)

Players.PlayerRemoving:Connect(function(player)
    local pid = player:GetAttribute("gsb_pid")
    if not pid then return end

    -- Save profile on leave
    local ds = gsb:GetDataStore("profile")
    ds:SetAsync(pid, {
        xp     = player:GetAttribute("xp"),
        level  = player:GetAttribute("level"),
        kills  = player:GetAttribute("kills"),
    })
    playerIds[player.UserId] = nil
end)

-- Called by your combat system
local function onPlayerKill(killerUserId)
    local pid = playerIds[killerUserId]
    if not pid then return end

    -- Award gold and increment kill counter
    gsb:AdjustEconomy(pid, {{ currency_id = GOLD_ID, amount = 25 }}, {})
    local killer = Players:GetPlayerByUserId(killerUserId)
    if killer then
        local kills = (killer:GetAttribute("kills") or 0) + 1
        killer:SetAttribute("kills", kills)
        gsb:SubmitScore("all-time-kills", pid, kills)
    end
end

Cross-Experience Data Sharing

The same ProjectID and EnvironmentID work across any number of Roblox experiences you own. A player verified in experience A has the same player_id and can access the same documents, balances, and leaderboards from experience B — no DataStore bridging needed.

-- Experience A (e.g. your main game)
local gsbA = GSB.init("proj_shared", "st_main_game", "env_prod")
gsbA:VerifyPlayer(player.UserId) -- creates player record

-- Experience B (e.g. your hub / lobby game)
local gsbB = GSB.init("proj_shared", "st_hub", "env_prod")
-- Same proj + env → same player record → same documents and leaderboards
local profile = gsbB:GetDataStore("profile"):GetAsync(pid)

Troubleshooting

Error Likely Cause Fix
HttpService is not enabled HTTP requests disabled in game settings Game Settings → Security → Allow HTTP Requests
API Error (401) Wrong or expired server token Check token in dashboard; make sure you're using the correct environment
API Error (404) Wrong project ID, environment ID, or resource doesn't exist yet Double-check IDs; create the leaderboard or currency in the dashboard first
API Error (429) Rate limit hit (unlikely in normal usage) The SDK retries automatically with backoff. Check your plan limits.
No data returned from GetAsync Player not verified yet, or document doesn't exist Call VerifyPlayer first; treat nil return as empty initial state

Next Steps

  • Dashboard player browser — search any player by GSB ID, view and edit their documents, see economy state, inspect audit logs. No script needed.
  • Seasonal leaderboards — configure a reset schedule in the dashboard. GSB archives the previous period automatically.
  • Social featuresgsb:SendFriendRequest, gsb:ListFriends for cross-experience friend lists that survive server restarts.
  • Config delivery — push a JSON bundle from the dashboard and read it in-experience with gsb:GetActiveConfig(), so you can tune balance values without republishing.

Related in This Hub

To create a project and grab your credentials, visit Supercraft Game Server Backend.

Top