Guide

Authentication

RoExpress authentication is built on the middleware system. Every request goes through middleware before the handler runs | return false to block with 403, return nothing to continue.

Global ban check

The simplest auth: block banned players from every route. Register this first so it runs before any other middleware.

local app = RoExpress.GetApp()
local banned = { [1234567] = true }  -- userId → banned

app:Use("ban-check", function(Player, Payload)
    if banned[Player.UserId] then
        return false  -- sends 403 Forbidden
    end
end)

Admin-only routes

Use a helper to create a middleware closure and register it per-route via a second Use call with a unique id, or use a naming convention to scope it.

local ADMINS = { [987654321] = true }

local function requireAdmin(Player)
    return ADMINS[Player.UserId] == true
end

-- Register a named middleware that only runs on /admin routes
app:Use("admin-gate", function(Player, Payload)
    if Payload.route:match("^admin/") then
        if not requireAdmin(Player) then return false end
    end
end)

app:Get("admin/stats", function(req, res)
    res:Send({ serverFPS = workspace:GetRealPhysicsFPS() })
end)

DataStore-backed ban list

local DataStoreService = game:GetService("DataStoreService")
local BanStore = DataStoreService:GetDataStore("Bans")
local banCache = {}

-- Warm cache on player join
game.Players.PlayerAdded:Connect(function(p)
    local ok, result = pcall(BanStore.GetAsync, BanStore, tostring(p.UserId))
    if ok and result then
        banCache[p.UserId] = true
        p:Kick("You are banned.")
    end
end)

app:Use("ban", function(Player) if banCache[Player.UserId] then return false end end)

Role-based middleware pattern

local Roles = {} -- { [userId] = "admin"|"mod"|"player" }

local function requireRole(role)
    return function(Player)
        if Roles[Player.UserId] ~= role then return false end
    end
end

-- Per-port with dedicated rate limits and role check
app:Listen("modtools", function(port)
    port:Use("role", requireRole("admin"))
    port:Delete("player/:id=number", function(req, res)
        -- only admins reach this
        res:Send(banPlayer(req.params.id))
    end)
end)

Error handling in middleware

If your middleware throws an unhandled error, RoExpress sends 500 and logs the stack trace. Wrap external calls (DataStore, ProfileService) in pcall:

app:Use("profile-check", function(Player)
    local ok, profile = pcall(getProfile, Player)
    if not ok or not profile then return false end
    -- attach to request context via Bridge if needed
end)

See also

App | full middleware API  ·  Request Pipeline | where middleware sits in the pipeline  ·  Tamper | passive exploit detection  ·  Ports | per-port middleware isolation