Documentation
¶
Overview ¶
Package endpoint defines the core Endpoint type and related helpers.
An Endpoint is the fundamental building block of the framework: a function that accepts a context and an arbitrary request value, and returns an arbitrary response value or an error. Business logic, middleware, and transport layers all communicate through this single interface.
Index ¶
- Variables
- func Nop(context.Context, any) (any, error)
- func RequestIDFromContext(ctx context.Context) string
- func WithRequestID(ctx context.Context, id string) context.Context
- func WithTraceID(ctx context.Context, id TraceID) context.Context
- type Builder
- func (b *Builder) Build() Endpoint
- func (b *Builder) Use(m Middleware) *Builder
- func (b *Builder) WithBackpressure(max int64) *Builder
- func (b *Builder) WithErrorHandling(operation string) *Builder
- func (b *Builder) WithLogging(logger *Logger, operation string) *Builder
- func (b *Builder) WithMetrics(m *Metrics) *Builder
- func (b *Builder) WithTimeout(d time.Duration) *Builder
- func (b *Builder) WithTracing() *Builder
- type Endpoint
- type EndpointCache
- type EndpointCloser
- type EndpointerOption
- type EndpointerOptions
- type ErrorWrapper
- type Factory
- type Failer
- type Logger
- type Metrics
- type Middleware
- func BackpressureMiddleware(max int64) Middleware
- func Chain(outer Middleware, others ...Middleware) Middleware
- func ErrorHandlingMiddleware(operation string) Middleware
- func InFlightMiddleware(max int64, counter *int64) Middleware
- func LoggingMiddleware(logger *log.Logger, operation string) Middleware
- func MetricsMiddleware(metrics *Metrics) Middleware
- func TimeoutMiddleware(d time.Duration) Middleware
- func TracingMiddleware() Middleware
- type SpanID
- type TraceID
- type TypeAssertError
- type TypedEndpoint
Constants ¶
This section is empty.
Variables ¶
var ErrBackpressure = errors.New("too many concurrent requests")
ErrBackpressure is returned when the concurrency limit is exceeded.
Functions ¶
func Nop ¶
Nop is a no-op Endpoint that always succeeds and returns an empty struct. Useful as a placeholder in tests or when an endpoint is not yet implemented.
func RequestIDFromContext ¶ added in v1.2.0
RequestIDFromContext extracts the request ID from the context.
func WithRequestID ¶ added in v1.2.0
WithRequestID injects a request ID into the context.
Types ¶
type Builder ¶ added in v1.2.0
type Builder struct {
// contains filtered or unexported fields
}
Builder provides a fluent API for assembling an Endpoint with a middleware chain. Middlewares are applied in the order they are added (outermost first), matching the behaviour of Chain().
Example:
ep := endpoint.NewBuilder(myEndpoint).
Use(loggingMiddleware).
Use(ratelimit.NewErroringLimiter(limiter)).
Use(circuitbreaker.Gobreaker(cb)).
Build()
func NewBuilder ¶ added in v1.2.0
NewBuilder creates a Builder wrapping the given base Endpoint.
func NewTypedBuilder ¶ added in v1.2.0
func NewTypedBuilder[Req, Resp any](te TypedEndpoint[Req, Resp]) *Builder
NewTypedBuilder creates a Builder from a TypedEndpoint. This is a convenience shorthand for endpoint.NewBuilder(te.Wrap()).
Example:
ep := endpoint.NewTypedBuilder(myTypedEndpoint).
WithTimeout(5 * time.Second).
Use(circuitbreaker.Gobreaker(cb)).
Build()
func (*Builder) Build ¶ added in v1.2.0
Build applies all middlewares and returns the final Endpoint. The Builder can be reused after calling Build.
func (*Builder) Use ¶ added in v1.2.0
func (b *Builder) Use(m Middleware) *Builder
Use appends a Middleware to the chain. Returns the same Builder for method chaining.
func (*Builder) WithBackpressure ¶ added in v1.2.0
WithBackpressure appends BackpressureMiddleware with the given concurrency limit.
func (*Builder) WithErrorHandling ¶ added in v1.2.0
WithErrorHandling appends an ErrorHandlingMiddleware for the named operation.
func (*Builder) WithLogging ¶ added in v1.2.0
WithLogging appends LoggingMiddleware for the named operation. This is a shorthand for Use(LoggingMiddleware(logger, operation)).
func (*Builder) WithMetrics ¶ added in v1.2.0
WithMetrics appends a MetricsMiddleware and returns the Metrics pointer so the caller can inspect counters later.
var m endpoint.Metrics ep := endpoint.NewBuilder(base).WithMetrics(&m).Build()
func (*Builder) WithTimeout ¶ added in v1.2.0
WithTimeout appends a TimeoutMiddleware that cancels the context after d. This is a shorthand for Use(TimeoutMiddleware(d)).
func (*Builder) WithTracing ¶ added in v1.2.0
WithTracing appends TracingMiddleware to the Builder.
type Endpoint ¶
Endpoint is a function that handles a single RPC-style request. It is the primary abstraction in the framework — every service method, middleware, and transport adapter is expressed in terms of Endpoint.
type EndpointCache ¶
type EndpointCache struct {
// contains filtered or unexported fields
}
EndpointCache maps service instance addresses to Endpoints. It is updated by Endpointer as service-discovery events arrive and optionally invalidates stale entries after a configurable grace period.
func NewEndpointCache ¶
func NewEndpointCache(factory Factory, logger *log.Logger, options EndpointerOptions) *EndpointCache
func (*EndpointCache) Endpoints ¶
func (c *EndpointCache) Endpoints() ([]Endpoint, error)
func (*EndpointCache) Update ¶
func (c *EndpointCache) Update(event events.Event)
type EndpointCloser ¶
type EndpointerOption ¶
type EndpointerOption func(*EndpointerOptions)
EndpointerOption is a functional option for EndpointerOptions.
func InvalidateOnError ¶
func InvalidateOnError(timeout time.Duration) EndpointerOption
InvalidateOnError returns an EndpointerOption that enables cache invalidation after a service-discovery error. The cache is cleared once timeout has elapsed, causing subsequent Endpoints() calls to return an error until healthy instances are re-discovered.
type EndpointerOptions ¶
type EndpointerOptions struct {
// InvalidateOnError, when true, causes the cache to be cleared after
// InvalidateTimeout has elapsed following a discovery error.
InvalidateOnError bool
// InvalidateTimeout is the grace period before the cache is cleared.
// Only meaningful when InvalidateOnError is true.
InvalidateTimeout time.Duration
}
EndpointerOptions configures the behaviour of an EndpointCache when a service-discovery error is received.
type ErrorWrapper ¶
ErrorWrapper wraps an endpoint error with the name of the operation that produced it, enabling callers to distinguish errors from different endpoints using errors.As.
func (*ErrorWrapper) Error ¶
func (e *ErrorWrapper) Error() string
func (*ErrorWrapper) Unwrap ¶
func (e *ErrorWrapper) Unwrap() error
type Factory ¶
Factory creates an Endpoint for a given service instance address. It is used by EndpointCache and Endpointer to build endpoints on demand as service-discovery events arrive.
The returned io.Closer (if non-nil) is called when the instance is removed from the cache, allowing the caller to release resources (e.g. close a gRPC connection).
type Failer ¶
type Failer interface {
Failed() error
}
Failer may be implemented by a response type to signal a business-logic error without using the Go error return value. If the response implements Failer and Failed() returns non-nil, the transport layer treats the call as failed.
When to use Failer ¶
Most services should return errors normally:
func (s *svc) CreateUser(ctx context.Context, req CreateUserRequest) (CreateUserResponse, error) {
if req.Name == "" {
return CreateUserResponse{}, errors.New("name required") // normal Go error
}
...
}
Use Failer only when the transport protocol requires a successful wire-level response even on business failure — for example, when a gRPC method must return a proto message (not a gRPC status error) to carry structured error details:
type CreateUserResponse struct {
User *User
Error string `json:"error,omitempty"`
err error // unexported, set by business logic
}
func (r CreateUserResponse) Failed() error { return r.err }
type Metrics ¶
type Metrics struct {
RequestCount int64
ErrorCount int64
SuccessCount int64
TotalDuration time.Duration
LastRequestTime time.Time
}
Metrics holds counters and timing data collected by MetricsMiddleware. All fields are updated in-place; use atomic reads if you need to observe them from a different goroutine.
type Middleware ¶
Middleware is a function that wraps an Endpoint to add cross-cutting concerns such as logging, metrics, rate limiting, or circuit breaking.
The recommended way to compose middlewares is via Builder:
ep := endpoint.NewBuilder(base).
Use(m1).Use(m2).Use(m3).
Build()
Chain is available for cases where a Middleware value itself is needed.
func BackpressureMiddleware ¶ added in v1.2.0
func BackpressureMiddleware(max int64) Middleware
BackpressureMiddleware returns a Middleware that limits the number of concurrent in-flight requests to max. When the limit is reached, new requests are rejected immediately with ErrBackpressure.
This is essential for large-scale systems to prevent cascading failures when a downstream service slows down.
Example:
// Allow at most 100 concurrent requests ep = endpoint.BackpressureMiddleware(100)(ep)
func Chain ¶
func Chain(outer Middleware, others ...Middleware) Middleware
Chain composes multiple Middlewares into a single Middleware. Prefer Builder.Use() for most use cases — it is more readable and supports named shortcuts (WithTimeout, WithMetrics, etc.).
The first argument is the outermost wrapper; subsequent arguments are applied inward:
outer → others[0] → others[1] → … → Endpoint
func ErrorHandlingMiddleware ¶
func ErrorHandlingMiddleware(operation string) Middleware
ErrorHandlingMiddleware returns a Middleware that wraps any error returned by the next Endpoint in an ErrorWrapper tagged with operation. Use errors.As to unwrap it downstream:
var ew *endpoint.ErrorWrapper
if errors.As(err, &ew) { ... }
func InFlightMiddleware ¶ added in v1.2.0
func InFlightMiddleware(max int64, counter *int64) Middleware
InFlightMiddleware is like BackpressureMiddleware but also exposes the current in-flight count via the provided pointer. Useful for metrics.
Example:
var inflight int64 ep = endpoint.InFlightMiddleware(100, &inflight)(ep) // inflight is updated atomically on every call
func LoggingMiddleware ¶ added in v1.2.0
func LoggingMiddleware(logger *log.Logger, operation string) Middleware
LoggingMiddleware returns a Middleware that logs each call to the wrapped Endpoint using the provided zap logger. It records:
- the operation name
- whether the call succeeded or failed
- the elapsed duration
Example:
logger, _ := log.NewDevelopment() ep = endpoint.LoggingMiddleware(logger, "CreateUser")(ep)
func MetricsMiddleware ¶
func MetricsMiddleware(metrics *Metrics) Middleware
MetricsMiddleware returns a Middleware that records per-endpoint metrics into the provided Metrics struct. It increments RequestCount on every call, SuccessCount when the next Endpoint returns nil error, and ErrorCount otherwise.
func TimeoutMiddleware ¶ added in v1.2.0
func TimeoutMiddleware(d time.Duration) Middleware
TimeoutMiddleware returns a Middleware that cancels the context after d. The wrapped endpoint receives a context that will be cancelled when the deadline is exceeded.
Example:
ep = endpoint.TimeoutMiddleware(5 * time.Second)(ep)
func TracingMiddleware ¶ added in v1.2.0
func TracingMiddleware() Middleware
TracingMiddleware returns a Middleware that propagates or generates a trace ID and request ID in the context.
If the context already contains a trace ID (injected by the transport layer from an incoming header), it is preserved. Otherwise a new one is generated.
This enables end-to-end request correlation across service boundaries without requiring an external tracing system.
Example:
ep = endpoint.TracingMiddleware()(ep) // In a handler, read the IDs: traceID := endpoint.TraceIDFromContext(ctx) reqID := endpoint.RequestIDFromContext(ctx)
type SpanID ¶ added in v1.2.0
type SpanID string
SpanID is a unique identifier for a single operation within a trace.
type TraceID ¶ added in v1.2.0
type TraceID string
TraceID is a unique identifier for a distributed trace.
func TraceIDFromContext ¶ added in v1.2.0
TraceIDFromContext extracts the trace ID from the context. Returns an empty string if not set.
type TypeAssertError ¶ added in v1.2.0
TypeAssertError is returned by Unwrap when the response value cannot be asserted to the expected type.
func (*TypeAssertError) Error ¶ added in v1.2.0
func (e *TypeAssertError) Error() string
type TypedEndpoint ¶ added in v1.2.0
TypedEndpoint is a type-safe variant of Endpoint that eliminates runtime type assertions. Req and Resp are the concrete request and response types.
Use Wrap to convert a TypedEndpoint into a plain Endpoint for use with middleware and transport layers. Use Unwrap to go the other direction.
Example:
type HelloReq struct { Name string }
type HelloResp struct { Message string }
var ep endpoint.TypedEndpoint[HelloReq, HelloResp] =
func(ctx context.Context, req HelloReq) (HelloResp, error) {
return HelloResp{Message: "Hello, " + req.Name}, nil
}
// Convert to plain Endpoint for middleware / transport
plain := ep.Wrap()
// Build with middleware, then call type-safely
typed := endpoint.Unwrap[HelloReq, HelloResp](
endpoint.NewBuilder(plain).
WithTimeout(5 * time.Second).
Build(),
)
resp, err := typed(ctx, HelloReq{Name: "world"})
func Unwrap ¶ added in v1.2.0
func Unwrap[Req, Resp any](ep Endpoint) TypedEndpoint[Req, Resp]
Unwrap wraps a plain Endpoint in a TypedEndpoint[Req, Resp]. The returned function type-asserts the response; it returns an error if the response cannot be asserted to Resp.
Use this after applying middleware to recover type safety:
typed := endpoint.Unwrap[HelloReq, HelloResp](
endpoint.NewBuilder(base).Use(myMiddleware).Build(),
)
resp, err := typed(ctx, HelloReq{Name: "world"})
func (TypedEndpoint[Req, Resp]) Wrap ¶ added in v1.2.0
func (te TypedEndpoint[Req, Resp]) Wrap() Endpoint
Wrap converts a TypedEndpoint into a plain Endpoint. The returned Endpoint performs a type assertion on the request value; it panics if the request is not of type Req.