gateway

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 25, 2026 License: MIT Imports: 20 Imported by: 0

README

gateway

A generic orchestration and gateway framework for routing one standard request format across multiple external providers.

Use this package to build gateways for payments, LLMs, notifications, storage, search, or any system that needs:

  • A unified application-facing interface.
  • Bidirectional payload translation.
  • Transport-independent custom providers.
  • Built-in routing strategies.
  • Retry and cross-provider fallback.
  • Request timeouts and structured logging.

A simple payment gateway can be setup as shown below:

paymentGateway, err := gw.New[ChargeRequest, ChargeResponse](
    gw.WithProviders(
        gw.UseProvider(
            fastPay,
            gw.WithProviderPriority(1),
            gw.WithProviderWeight(70),
            gw.WithProviderCost(0.029),
        ),
        gw.UseProvider(
            safePay,
            gw.WithProviderPriority(2),
            gw.WithProviderWeight(30),
            gw.WithProviderCost(0.032),
        ),
    ),
    gw.WithRouting(gw.PowerOfTwo(gw.ByObservedLatency())),
    gw.WithFallback(gw.FallbackOn(
        gw.ErrorRateLimited,
        gw.ErrorTimeout,
        gw.ErrorUnavailable,
    )),
    gw.WithRetry(gw.Retry{
        MaxAttempts: 2,
        Backoff: gw.ExponentialBackoff{
            Initial: 100 * time.Millisecond,
            Maximum: time.Second,
        },
    }),
    gw.WithRequestTimeout(10*time.Second),
    gw.WithLogger(slog.Default()),
)

if err != nil {
    log.Fatal(err)
}

The application calls one method:

result, err := paymentGateway.HandleRequest(ctx, gw.Request[ChargeRequest]{
    Operation: "payment.charge",
    Payload:   charge,
})

Installation

  • Go 1.23 or later.
  • No third-party runtime dependencies.
go get github.com/henryhale/gateway

Documentation

See docs/index.md for the full guide.

Development

Run all release checks:

make check

Individual commands:

go fmt ./...
go vet ./...
go test -race ./...

License

MIT. See LICENSE for details.

Documentation

Overview

package gateway provides a transport-independent gateway and orchestration framework.

Applications define standard request and response models, implement one provider adapter or HTTP codec per external service, select a built-in routing strategy, and expose the resulting Gateway through any web framework, RPC server, worker, command-line application, or background process.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Attempt

type Attempt struct {
	Provider  string
	Number    int
	StartedAt time.Time
	Duration  time.Duration
	ErrorKind ErrorKind
	ErrorCode ErrorCode
	Success   bool
}

Attempt describes one provider invocation made while handling a request.

type BackoffPolicy

type BackoffPolicy interface {
	// Delay returns the wait duration before the supplied retry number.
	Delay(retryNumber int) time.Duration
}

BackoffPolicy calculates the delay before a retry attempt.

type CandidateScorer

type CandidateScorer interface {
	// Score returns a lower-is-better score for a candidate.
	Score(candidate ProviderState) float64
}

CandidateScorer assigns a lower-is-better score to a provider candidate.

func ByCost

func ByCost() CandidateScorer

ByCost creates a scorer that prefers lower configured cost.

func ByObservedLatency

func ByObservedLatency() CandidateScorer

ByObservedLatency creates a scorer that prefers lower observed latency.

type Codec

type Codec[RequestPayload any, ResponsePayload any] interface {
	// Supports reports whether the codec can translate an operation.
	Supports(operation Operation) bool

	// Encode translates a standard request into an HTTP provider request.
	Encode(
		ctx context.Context,
		request Request[RequestPayload],
	) (HTTPRequest, error)

	// Decode translates an HTTP provider response into the standard response.
	Decode(
		ctx context.Context,
		response HTTPResponse,
	) (ResponsePayload, error)
}

Codec translates between the application's standard format and an HTTP provider format.

type ErrorCode

type ErrorCode string

ErrorCode identifies the specific reason behind a GatewayError, scoped within its Kind.

const (
	// CodeAdapterError indicates an unclassified error returned by a provider adapter.
	CodeAdapterError ErrorCode = "adapter_error"
	// CodeNilGateway indicates a request was made on a nil Gateway.
	CodeNilGateway ErrorCode = "nil_gateway"
	// CodeOperationRequired indicates a request was submitted without an operation.
	CodeOperationRequired ErrorCode = "operation_required"
	// CodeProviderHintUnknown indicates a request specified an unregistered provider hint.
	CodeProviderHintUnknown ErrorCode = "provider_hint_unknown"
	// CodeProviderHintUnsupported indicates a request's provider hint does not support the operation.
	CodeProviderHintUnsupported ErrorCode = "provider_hint_unsupported"
	// CodeOperationUnsupported indicates no registered provider supports the requested operation.
	CodeOperationUnsupported ErrorCode = "operation_unsupported"
	// CodeRoutingFailed indicates the routing strategy failed to select a provider.
	CodeRoutingFailed ErrorCode = "routing_failed"
	// CodeRoutingUnknownProvider indicates the routing strategy selected an unregistered provider.
	CodeRoutingUnknownProvider ErrorCode = "routing_unknown_provider"
	// CodeRetryLoopExhausted indicates the provider retry loop ended without a result.
	CodeRetryLoopExhausted ErrorCode = "retry_loop_exhausted"
	// CodeRequestCanceled indicates the caller canceled the request context.
	CodeRequestCanceled ErrorCode = "request_canceled"
	// CodeRequestTimeout indicates the request context deadline was exceeded.
	CodeRequestTimeout ErrorCode = "request_timeout"
	// CodeNilCodec indicates an HTTP provider was configured without a codec.
	CodeNilCodec ErrorCode = "nil_codec"
	// CodeInvalidBaseURL indicates an HTTP provider was configured with an invalid base URL.
	CodeInvalidBaseURL ErrorCode = "invalid_base_url"
	// CodeResponseReadFailed indicates the HTTP provider response body could not be read.
	CodeResponseReadFailed ErrorCode = "response_read_failed"
	// CodeResponseTooLarge indicates the HTTP provider response exceeded the configured size limit.
	CodeResponseTooLarge ErrorCode = "response_too_large"
	// CodeTransportError indicates an unclassified HTTP transport failure.
	CodeTransportError ErrorCode = "transport_error"
	// CodeNetworkTimeout indicates a lower-level network operation timed out.
	CodeNetworkTimeout ErrorCode = "network_timeout"
)

type ErrorKind

type ErrorKind string

ErrorKind classifies failures independently of any provider or transport.

const (
	// ErrorValidation indicates invalid application input.
	ErrorValidation ErrorKind = "validation"
	// ErrorAuthentication indicates invalid or missing provider credentials.
	ErrorAuthentication ErrorKind = "authentication"
	// ErrorAuthorization indicates insufficient provider permissions.
	ErrorAuthorization ErrorKind = "authorization"
	// ErrorRateLimited indicates provider throttling.
	ErrorRateLimited ErrorKind = "rate_limit"
	// ErrorTimeout indicates a provider or request deadline was exceeded.
	ErrorTimeout ErrorKind = "timeout"
	// ErrorUnavailable indicates a provider could not serve the request.
	ErrorUnavailable ErrorKind = "unavailable"
	// ErrorRejected indicates a provider rejected a valid operation.
	ErrorRejected ErrorKind = "rejected"
	// ErrorCanceled indicates the caller canceled the request.
	ErrorCanceled ErrorKind = "canceled"
	// ErrorInternal indicates a framework or adapter failure.
	ErrorInternal ErrorKind = "internal"
	// ErrorUnknown indicates an unclassified provider failure.
	ErrorUnknown ErrorKind = "unknown"
)

type ExponentialBackoff

type ExponentialBackoff struct {
	Initial time.Duration
	Maximum time.Duration
	Jitter  float64
}

ExponentialBackoff implements capped exponential retry delays with optional jitter.

func (ExponentialBackoff) Delay

func (b ExponentialBackoff) Delay(retryNumber int) time.Duration

Delay returns the capped exponential delay for a retry number.

type FallbackPolicy

type FallbackPolicy interface {
	// Allows reports whether fallback is permitted for an error.
	Allows(err *GatewayError) bool
}

FallbackPolicy decides whether an error may be retried on another provider.

func FallbackOn

func FallbackOn(kinds ...ErrorKind) FallbackPolicy

FallbackOn creates a policy that allows fallback for selected error kinds.

type Gateway

type Gateway[RequestPayload any, ResponsePayload any] struct {
	// contains filtered or unexported fields
}

Gateway routes standard requests through registered providers.

func New

func New[RequestPayload any, ResponsePayload any](
	options ...Option,
) (*Gateway[RequestPayload, ResponsePayload], error)

New constructs a ready-to-use Gateway from providers and built-in policies.

func (*Gateway[RequestPayload, ResponsePayload]) HandleRequest

func (g *Gateway[RequestPayload, ResponsePayload]) HandleRequest(
	ctx context.Context,
	request Request[RequestPayload],
) (Result[ResponsePayload], error)

HandleRequest routes one standard request and returns one standard response.

type GatewayError

type GatewayError struct {
	Kind         ErrorKind
	Code         ErrorCode
	Message      string
	Provider     string
	Operation    Operation
	Attempt      int
	StatusCode   int
	Retryable    bool
	Fallbackable bool
	Cause        error
}

GatewayError is the normalized error returned by the framework.

func AsError

func AsError(err error) (*GatewayError, bool)

AsError extracts a GatewayError from an error chain.

func HTTPProviderError

func HTTPProviderError(statusCode int, code ErrorCode, message string) *GatewayError

HTTPProviderError creates a normalized error from an HTTP status response.

func (*GatewayError) Error

func (e *GatewayError) Error() string

Error returns a stable human-readable gateway error message.

func (*GatewayError) Unwrap

func (e *GatewayError) Unwrap() error

Unwrap returns the underlying adapter or transport error.

type HTTPProviderConfig

type HTTPProviderConfig struct {
	BaseURL          string
	Headers          http.Header
	Timeout          time.Duration
	MaxResponseBytes int64
	Client           *http.Client
}

HTTPProviderConfig configures an HTTP-backed provider.

type HTTPRequest

type HTTPRequest struct {
	Method  string
	Path    string
	Query   url.Values
	Headers http.Header
	Body    []byte
}

HTTPRequest describes the provider-specific request produced by a Codec.

type HTTPResponse

type HTTPResponse struct {
	StatusCode int
	Headers    http.Header
	Body       []byte
}

HTTPResponse describes the raw provider response supplied to a Codec.

type Operation

type Operation string

Operation identifies a provider capability such as payment.charge or llm.chat.

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option configures a Gateway during construction.

func WithFallback

func WithFallback(policy FallbackPolicy) Option

WithFallback selects the cross-provider fallback policy.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger installs a custom structured logger.

func WithProviders

func WithProviders[RequestPayload any, ResponsePayload any](
	providers ...ProviderRegistration[RequestPayload, ResponsePayload],
) Option

WithProviders registers one or more typed providers with a Gateway.

func WithRequestTimeout

func WithRequestTimeout(timeout time.Duration) Option

WithRequestTimeout sets the maximum duration for one HandleRequest call.

func WithRetry

func WithRetry(retry Retry) Option

WithRetry configures same-provider retry behavior.

func WithRouting

func WithRouting(strategy RoutingStrategy) Option

WithRouting selects the routing strategy used by a Gateway.

type Provider

type Provider[RequestPayload any, ResponsePayload any] interface {
	// Name returns the unique provider registration name.
	Name() string

	// Supports reports whether the provider can execute an operation.
	Supports(operation Operation) bool

	// Execute translates, sends, receives, and normalizes one request.
	Execute(
		ctx context.Context,
		request Request[RequestPayload],
	) (ResponsePayload, error)
}

Provider is the complete transport-independent provider contract.

func NewHTTPProvider

func NewHTTPProvider[RequestPayload any, ResponsePayload any](
	name string,
	config HTTPProviderConfig,
	codec Codec[RequestPayload, ResponsePayload],
) Provider[RequestPayload, ResponsePayload]

NewHTTPProvider creates a transport-independent Provider backed by net/http.

type ProviderOption

type ProviderOption interface {
	// contains filtered or unexported methods
}

ProviderOption configures routing metadata for one provider registration.

func WithProviderCost

func WithProviderCost(cost float64) ProviderOption

WithProviderCost assigns a normalized per-request cost used by cost routing.

func WithProviderMetadata

func WithProviderMetadata(metadata map[string]string) ProviderOption

WithProviderMetadata adds immutable routing metadata to a provider.

func WithProviderPriority

func WithProviderPriority(priority int) ProviderOption

WithProviderPriority assigns a lower-is-preferred priority to a provider.

func WithProviderWeight

func WithProviderWeight(weight int) ProviderOption

WithProviderWeight assigns a relative weight to a provider.

type ProviderRegistration

type ProviderRegistration[RequestPayload any, ResponsePayload any] struct {
	// contains filtered or unexported fields
}

ProviderRegistration binds a provider to routing metadata.

func UseProvider

func UseProvider[RequestPayload any, ResponsePayload any](
	provider Provider[RequestPayload, ResponsePayload],
	options ...ProviderOption,
) ProviderRegistration[RequestPayload, ResponsePayload]

UseProvider prepares a provider for registration with a Gateway.

type ProviderState

type ProviderState struct {
	Name            string
	Priority        int
	Weight          int
	Cost            float64
	ObservedLatency time.Duration
	Healthy         bool
	Metadata        map[string]string
}

ProviderState describes a provider candidate presented to a routing strategy.

type Request

type Request[T any] struct {
	ID             string
	Operation      Operation
	IdempotencyKey string
	ProviderHint   string
	Payload        T
	Metadata       map[string]string
}

Request contains the standard application payload submitted to a Gateway.

type Result

type Result[T any] struct {
	RequestID string
	Provider  string
	Payload   T
	Attempts  []Attempt
	Usage     Usage
}

Result contains the normalized response returned by a Gateway.

type Retry

type Retry struct {
	MaxAttempts int
	Backoff     BackoffPolicy
}

Retry configures same-provider retry behavior.

type RoutingStrategy

type RoutingStrategy interface {
	// Name returns the strategy identifier used in diagnostics.
	Name() string

	// Select chooses one candidate or returns an error when none can be selected.
	Select(ctx context.Context, candidates []ProviderState) (ProviderState, error)
}

RoutingStrategy selects one provider from the currently eligible candidates.

func LowestCost

func LowestCost() RoutingStrategy

LowestCost creates a strategy that selects the least expensive provider.

func PowerOfTwo

func PowerOfTwo(scorer CandidateScorer) RoutingStrategy

PowerOfTwo creates a strategy that samples two candidates and chooses the better score.

func Priority

func Priority(providerNames ...string) RoutingStrategy

Priority creates a strategy that follows the supplied provider order.

func RoundRobin

func RoundRobin() RoutingStrategy

RoundRobin creates a concurrency-safe round-robin routing strategy.

func Weighted

func Weighted(weights map[string]int) RoutingStrategy

Weighted creates a weighted-random routing strategy.

type Usage

type Usage struct {
	InputUnits  int64
	OutputUnits int64
	Cost        float64
	Currency    string
}

Usage records optional normalized resource consumption reported by a provider.

Directories

Path Synopsis
internal
testprovider
Package testprovider provides deterministic providers for gateway tests.
Package testprovider provides deterministic providers for gateway tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL