-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Question Description
Background
In my application, I use a custom "Unified Action" API specification instead of traditional RESTful style. All API endpoints use a single POST /api
route, and requests are differentiated by the following structure:
{
"resource": "sys/user",
"action": "create",
"version": "v1",
"params": {
// ...
}
}
The combination of resource
, action
, and version
serves as the unique identifier for each API endpoint.
Current Challenge
This architecture creates a challenge when using the rate limiter middleware. Since there's only one middleware instance but I need to apply different rate limiting configurations for different APIs, I currently can only dynamically configure the MaxFunc
but not the Expiration
.
Here's my current rate limiter configuration:
limiter.New(limiter.Config{
LimiterMiddleware: limiter.SlidingWindow{},
MaxFunc: func(ctx fiber.Ctx) int {
request := contextx.APIRequest(ctx)
definition := manager.Lookup(request.Identifier)
return lo.Ternary(definition.HasRateLimit(), definition.RateLimit, 10)
},
Expiration: 30 * time.Second,
// The following is what I need but doesn't exist yet
// ExpirationFunc: func(ctx fiber.Ctx) time.Duration {
// request := contextx.APIRequest(ctx)
// definition := manager.Lookup(request.Identifier)
// return lo.Ternary(definition.HasRateLimit(), definition.RateExpiration, 30*time.Second)
// },
SkipFailedRequests: false,
SkipSuccessfulRequests: false,
KeyGenerator: func(ctx fiber.Ctx) string {
request := contextx.APIRequest(ctx)
var sb strings.Builder
_, _ = sb.WriteString(request.Resource)
_ = sb.WriteByte(constants.ByteColon)
_, _ = sb.WriteString(request.Version)
_ = sb.WriteByte(constants.ByteColon)
_, _ = sb.WriteString(request.Action)
_ = sb.WriteByte(constants.ByteColon)
_, _ = sb.WriteString(utils.GetIP(ctx))
_ = sb.WriteByte(constants.ByteColon)
principal := contextx.Principal(ctx)
if principal == nil {
principal = security.PrincipalAnonymous
}
_, _ = sb.WriteString(principal.Id)
return sb.String()
},
LimitReached: func(ctx fiber.Ctx) error {
r := &result.Result{
Code: result.ErrCodeTooManyRequests,
Message: i18n.T(result.ErrMessageTooManyRequests),
}
return r.ResponseWithStatus(ctx, fiber.StatusTooManyRequests)
},
})
What I'm Considering
I'm wondering if it would be possible to add an ExpirationFunc
configuration option, similar to how MaxFunc
works for rate limits.
From my understanding of the current implementation, supporting both dynamic MaxFunc
and ExpirationFunc
might be challenging. I'm thinking the ExpirationFunc
could potentially be called periodically to apply different expiration configurations, though this would introduce some delay.
I'd appreciate any insights or guidance on this approach before potentially submitting a formal feature request.
Thank you!
Code Snippet (optional)
package main
import "github.com/gofiber/fiber/v3"
import "log"
func main() {
app := fiber.New()
// An example to describe the question
log.Fatal(app.Listen(":3000"))
}
Checklist:
- I agree to follow Fiber's Code of Conduct.
- I have checked for existing issues that describe my questions prior to opening this one.
- I understand that improperly formatted questions may be closed without explanation.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status