Guide

Middleware

Middleware runs before every handler. Return false to reject with a 403, return nothing to pass through.

Signature

app:Use(name, function(Player, Payload) → boolean?)

Global blocking

Runs on every request. Good for banning players or blocking suspended accounts.

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

Route-scoped guards

Check Payload.route to run logic only for a subset of routes.

app:Use("admin-guard", function(Player, Payload)
    if Payload.route:match("^admin/") and not ADMINS[Player.UserId] then
        return false
    end
end)

Analytics / logging

No return value | passes through after recording.

app:Use("analytics", function(Player, Payload)
    Analytics:Track({
        userId  = Player.UserId,
        route   = Payload.route,
        method  = Payload.method,
        ts      = os.time(),
    })
end)

Middleware order

Middleware runs in registration order. Register guards before logging so rejected requests aren't tracked as successful.

app:Use("ban-check",   banCheckFn)    -- 1st
app:Use("admin-guard", adminGuardFn)  -- 2nd
app:Use("analytics",   analyticsFn)   -- 3rd | only runs if not rejected
Return false, not nil. Returning nil (or nothing) passes through. Only a literal false rejects.

See also

App | app:Use() API  ·  Authentication | full auth middleware with tokens  ·  Admin Commands | route-scoped guard in practice  ·  Tamper | built-in exploit detection