gofr icon indicating copy to clipboard operation
gofr copied to clipboard

Add AddOptionWithValidation interface to support error reporting in service options

Open Umang01-hash opened this issue 2 months ago • 4 comments

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.

Umang01-hash avatar Oct 06 '25 08:10 Umang01-hash

this seems to be a breaking change, can you add some examples if not

aryanmehrotra avatar Oct 23 '25 06:10 aryanmehrotra

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 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.

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.

Umang01-hash avatar Oct 23 '25 06:10 Umang01-hash

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 avatar Oct 23 '25 06:10 aryanmehrotra

@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.
  • NewHTTPServiceWithValidation is 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().

Umang01-hash avatar Oct 23 '25 07:10 Umang01-hash

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?

aryanmehrotra avatar Nov 18 '25 10:11 aryanmehrotra

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.

akshat-kumar-singhal avatar Nov 18 '25 11:11 akshat-kumar-singhal

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.

coolwednesday avatar Nov 18 '25 12:11 coolwednesday

Closing this issue as a new ticket to track the required implementation in GoFr's v2 has been added.

coolwednesday avatar Nov 18 '25 13:11 coolwednesday