Middleware
Owl runs every incoming request through a cascade of middleware before it ever reaches your Service's method, the mechanism behind RateLimiter and TypeChecker and one you can also write your own filters for.
Client sends a request
│
├── Global Middleware (Inbound) - defined in Owl.Start()
├── Service Middleware (Inbound) - defined in CreateService()
├── Remote Middleware (Inbound) - defined in CreateSignal/Property()
│
└── Service method executesA middleware is a function with the signature (plr, args) -> (boolean, ...any):
- returning
truelets the request through to the next middleware and eventually the Service method - returning
falserejects the request immediately optionally with a message as the second return value, useful for logging why
Because they run in a cascade, the earliest middleware to reject a request is the only one that runs a global rate limiter rejecting a spammed request means your Service-specific middleware (and the method itself) never executes at all.
Global middleware
Defined via Owl.Config or inline in Owl.Start(). Applied to every remote, on every Service.
-- > // Method A: via Owl.Config (before Start)
Owl.Config.GlobalMiddleware = {
Inbound = {
Owl.Util.RateLimiter.perPlayer(30, 1, "Global"),
},
Outbound = {},
}
-- > // Method B: directly inside Owl.Start()
Owl.Start({
GlobalMiddleware = {
Inbound = {
Owl.Util.RateLimiter.perPlayer(30, 1, "Global"),
},
},
}):catch(warn)Reach for global middleware for anything that should apply uniformly no matter which Service or remote is being hit a baseline per-player rate limit being the canonical example, since without one, any single remote you forget to protect individually becomes an open door.
Per-Service middleware
Applied only to a specific Service's remotes, in addition to whatever global middleware already ran.
local SecureService = Owl.CreateService({
Name = "SecureService",
Middleware = {
Inbound = {
function(plr: Player, args: {any}): (boolean, ...any)
if plr:GetAttribute("IsBanned") then
return false, "Player is banned."
end
return true
end,
},
},
Client = { --[[ ... ]] },
})This is the right level for checks that only make sense for one Service in particular a trading Service checking a ban flag, for instance, has no reason to run on every single remote in the game.
Outbound middleware follows the same signature and cascade, just on the way out useful for logging or transforming a value right before it's sent to the client, rather than filtering what comes in.
Where to go next
- RateLimiter - the built-in middleware behind
Owl.Util.RateLimiterand its full configuration options. - TypeChecker - validating a remote's arguments as middleware, instead of by hand at the top of every method.