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