Add AddOptionWithValidation interface to support error reporting in service options
Is your feature request related to a problem? Please describe. Our current service.Options interface doesn't support error reporting. This creates a risk of silent failures when invalid configurations are used as options.
package service
type Options interface {
AddOption(h HTTP) HTTP
}
When using AddOption, validation errors cannot be surfaced to the caller. Consumers must use separate validation methods or constructors, creating a disconnected validation flow.
Describe the solution you'd like
Create a new interface AddOptionWithValidation that returns an error:
type AddOptionWithValidation interface {
AddOptionWithValidation(h HTTP) (HTTP, error)
}
Services can check for this interface before falling back to the standard Options interface, enabling proper error handling while maintaining backward compatibility.
this seems to be a breaking change, can you add some examples if not
This is not a breaking change because we're adding a new interface alongside the existing one, not replacing it. Here's how it works:
Backward Compatibility:
- The original
Optionsinterface remains unchanged. - Existing code using
Options.AddOption()continues to work exactly as before. - The framework tries the new interface first, then falls back to the old one.
Implementation Example:
- New introduced interface:
package service
type Options interface {
AddOption(h HTTP) HTTP
}
// AddOptionWithValidation extends Options with error reporting capability
type AddOptionWithValidation interface {
AddOptionWithValidation(h HTTP) (HTTP, error)
}
- New method NewHTTPServiceWithValidation:
// NewHTTPServiceWithValidation provides error handling for options
func NewHTTPServiceWithValidation(serviceAddress string, logger Logger, metrics Metrics, options ...interface{}) (HTTP, error) {
h := &httpService{
Client: &http.Client{},
url: serviceAddress,
Tracer: otel.Tracer("gofr-http-client"),
Logger: logger,
Metrics: metrics,
}
var svc HTTP = h
// Process options with validation support
for _, o := range options {
// Try validation interface first
if validator, ok := o.(AddOptionWithValidation); ok {
var err error
svc, err = validator.AddOptionWithValidation(svc)
if err != nil {
return nil, err
}
} else if option, ok := o.(Options); ok {
// Fall back to standard interface
svc = option.AddOption(svc)
} else {
return nil, fmt.Errorf("option %T doesn't implement either Options or AddOptionWithValidation", o)
}
}
return svc, nil
}
Usage Example:
// Before - errors silently ignored
app.AddHTTPService("service1", "http://example.com", &service.RateLimiterConfig{Requests: -5})
// After - errors properly reported
err := app.AddHTTPServiceWithValidation("service1", "http://example.com", &service.RateLimiterConfig{Requests: -5})
if err != nil {
// Handle error: "invalid request rate: -5"
}
This approach prevents the silent failures we've been seeing with invalid configurations while maintaining complete backward compatibility.
The original Options interface remains unchanged. Existing code using Options.AddOption() continues to work exactly as before. The framework tries the new interface first, then falls back to the old one.
If this is the case then we dont need to actually have a fallback mechanism because we have a different method altogether - AddHTTPServiceWithValidation based on this it should work fine - this being said the current options will not work and at the same time AddHTTPServiceWithValidation is not doing exactly what is said if we allow both.
Also in the new method options ...interface{} is too generic and not an idiomatic go approach.
@aryanmehrotra existing Options and current constructors keep working.
- If callers keep using the old
NewHTTPService(or app.AddHTTPService) with types that implement Options, nothing breaks. NewHTTPServiceWithValidationis opt-in. It exists to let callers get validation errors when they want them.- The only reason to keep a runtime fallback from AddOptionWithValidation → Options is if you expect mixed/new code to pass both old and new option implementations to the same call.
Also I thought we can have ValidatorOption interface instead of new AddOptionWithValidation:
package service
type Options interface {
AddOption(h HTTP) HTTP
}
type ValidatorOption interface {
Options
Validate() error
}
We can make NewHTTPServiceWithValidation accept options ...Options (typed variadic). For each option, if it implements ValidatorOption call Validate() first; then call AddOption().
We need to think about this from a broader perspective.
Right now, our approach is to allow silent failures and rely on logging. The user already receives those logs and can refer to them, so the system remains resilient even when parts of the configuration are not perfect.
If we switch to strict validation and start failing on every invalid config, the service will only run when 100% of the configuration is correct. That means even a small misconfiguration will prevent the server from starting, which can quickly become a pain point for users. In many real-world scenarios, users may not want or need every single option to be perfectly configured all the time — partial functionality is often acceptable.
So before introducing validation-driven failure, we should decide whether we actually want to change this fundamental behaviour or keep the current flexibility.
@vikash @gizmo-rt @akshat-kumar-singhal @coolwednesday what do you guys think should we continue with how we do right now or we should be changing the interfaces?
Counter view: In case there's an error in the initialisation which was logged and application launched, subsequent operations would not work as intended and the developer is left digging for the root cause. Given that the warning logs come only during the start of the app, developer would need to know that they should
Recommendation: We should allow the developer to decide if they want to enforce strict validation or allow the app to run in case of these validation failures. This would be particularly helpful for the production environments where strict configuration would be preferred.
Counter view: In case there's an error in the initialisation which was logged and application launched, subsequent operations would not work as intended and the developer is left digging for the root cause. Given that the warning logs come only during the start of the app, developer would need to know that they should
Recommendation: We should allow the developer to decide if they want to enforce strict validation or allow the app to run in case of these validation failures. This would be particularly helpful for the production environments where strict configuration would be preferred.
My Perspective
I agree with the direction, but I’d prefer that this feature not be implemented specifically for the rate limiter alone. Instead, this should be part of a broader, comprehensive HTTP Service configuration strategy—covering auth, headers, rate limiting, and all other optional HTTP features uniformly.
For now, this PR can address the rate limiter behaviour as intended. But for v2, we should propose a unified and robust validation mechanism across all HTTP features. Implementing strict validation for one feature and silent-failure behaviour for others makes the overall HTTP service feel inconsistent and unpredictable.
Developers shouldn’t have to remember which optional features require strict attention and which ones silently fail. Either all optional features follow strict validation, or all follow soft-validation—consistency is key. The current mix forces developers to be cautious in some places and not in others, increasing cognitive overhead and onboarding friction.
In summary: ✔ This PR → proceed with the rate-limiter change ✔ v2 → design a coherent, consistent validation strategy across all HTTP service features
Let me know, if that works for you, we will create a issue ticket to track this.
Closing this issue as a new ticket to track the required implementation in GoFr's v2 has been added.