Documentation
¶
Overview ¶
Package gas is the core of the Gas framework: the DI container, the Worker and App lifecycles, the Router with service ownership, the EventBus, request scopes, the error model, the runtime kill switch, the provider interfaces (Logger, CacheProvider, ...) that other gasmod modules implement, and the Principal that authentication middleware stores on the request context.
Wiring ¶
NewWorker registers the *EventBus and the Worker itself, as HealthProvider and ReadyProvider, in its container; NewApp also registers its *Router. A type that implements Service has Init(ctx) called after construction and Close(ctx) at shutdown (singletons) or scope end (scoped); a type that declares Init or Close with any other signature is rejected at startup.
Errors from dependency resolution wrap one of the sentinels ErrNotRegistered, ErrCircularDependency, ErrCaptiveDependency, ErrScopeRequired, ErrNoConstructor, ErrInvalidLifetime, and ErrInvalidServiceShape, so callers classify them with errors.Is.
The container is safe for concurrent resolution. A singleton is built by one goroutine at a time: other goroutines asking for it while it is built wait and get the same instance, so its constructor and Init run once. Besides the cycles BuildAll finds in constructor parameters, resolution catches a constructor or Init that resolves the type being built through a captured container or scope, and two builds on different goroutines that would wait on each other; both return ErrCircularDependency naming the types. To tell a re-entrant resolution from a concurrent one, the container reads the calling goroutine's ID from runtime.Stack, once per constructor call and once per wait.
Lifecycle ¶
Every lifecycle method takes the caller's context: Worker.Start, InitServices, Shutdown, CloseService and RestartService, App.Start and App.Stop, ServiceContainer.BuildAll and NewScope, and Scope.Close. Run uses context.Background().
Each singleton's Init gets the caller's context plus an init deadline, 15 seconds by default (see WithServiceInitTimeout, WithServiceContainerServiceTimeout, and ServiceInitTimeout; 0 disables it). When the deadline passes, the container cancels Init's context and waits for Init to return, then fails with an error wrapping context.DeadlineExceeded, so no Init is left running behind a failed start. An Init that ignores the cancellation and returns nil late is closed again. Init's context is canceled when Init returns, so background work a service starts in Init must not derive from it. Scoped services get the scope's context with no added deadline; in an App that is the request context, and Close gets it without its cancellation, so scoped services clean up even after the client has gone.
A Start that fails closes, in reverse order, every service it had initialized, with its context stripped of cancellation, and returns the startup error joined with any Close errors. It leaves nothing running, and the Worker cannot be started again: later calls to Start, and to InitServices after a failed InitServices, return the same error.
Worker.Shutdown emits SystemShuttingDown and closes every service still active, in reverse initialization order, passing its context to Close. Services already closed by CloseService are skipped. A Close error does not stop the rest; all of them are returned, joined. Shutdown runs once, and later calls return nil. Once it has begun, CloseService and RestartService return an error. App.Stop first emits SystemServerShuttingDown and shuts the http.Server down within ServerSettings.ShutdownTimeout, or sooner if its context ends.
See the module README for usage examples and design rationale.
SPDX-License-Identifier: MIT
Index ¶
- Constants
- Variables
- func ApplyEnqueueOptions(opts []EnqueueOption) (delay time.Duration, groupID, dedupeID string, attrs map[string]string)
- func CacheControl(opt ...CacheControlOption) func(next http.Handler) http.Handler
- func MetadataValue[T any](m PrincipalMetadata, key string) (T, bool)
- func MustResolveFromRequestScope[T any](r *http.Request) T
- func NewNopLogger() func() (*NopLogger, error)
- func RequestLogger[T Logger](opt ...RequestLoggerOption) func(next http.Handler) http.Handler
- func ResolveFromRequestScope[T any](r *http.Request) (T, error)
- func SecurityHeaders(opt ...SecurityHeadersOption) func(next http.Handler) http.Handler
- func WantsJSON(r *http.Request) bool
- func WithLogger(ctx context.Context, logger Logger) context.Context
- func WithPrincipal(ctx context.Context, principal Principal) context.Context
- func WithRequestScope(ctx context.Context, scope *Scope) context.Context
- func WriteError(w http.ResponseWriter, r *http.Request, err error) error
- type App
- type AppConfigUpdated
- type AppConfigUpdatedPayload
- type AppOption
- type BasePrincipalMetadata
- type BindOption
- type BindOptions
- type CacheControlOption
- func WithCacheControlImmutable() CacheControlOption
- func WithCacheControlMaxAge(val time.Duration) CacheControlOption
- func WithCacheControlMustRevalidate() CacheControlOption
- func WithCacheControlMustUnderstand() CacheControlOption
- func WithCacheControlNoCache() CacheControlOption
- func WithCacheControlNoStore() CacheControlOption
- func WithCacheControlNoTransform() CacheControlOption
- func WithCacheControlPath(val string) CacheControlOption
- func WithCacheControlPathPrefix(val string) CacheControlOption
- func WithCacheControlPathPrefixes(val []string) CacheControlOption
- func WithCacheControlPathSuffix(val string) CacheControlOption
- func WithCacheControlPathSuffixes(val []string) CacheControlOption
- func WithCacheControlPaths(val []string) CacheControlOption
- func WithCacheControlPrivate() CacheControlOption
- func WithCacheControlProxyRevalidate() CacheControlOption
- func WithCacheControlPublic() CacheControlOption
- func WithCacheControlSMaxAge(val time.Duration) CacheControlOption
- func WithCacheControlStaleIfError(val time.Duration) CacheControlOption
- func WithCacheControlStaleWhileRevalidate(val time.Duration) CacheControlOption
- type CacheControlOptions
- type CacheProvider
- type Config
- type ConfigProvider
- type Context
- type ContextOption
- type DatabaseProvider
- type Email
- type EmailProvider
- type EnqueueOption
- type Environment
- type Error
- func AsError(err error) (*Error, bool)
- func BadRequest(message string) *Error
- func Conflict(message string) *Error
- func Forbidden(message string) *Error
- func Internal(message string) *Error
- func NewError(status int, code, message string) *Error
- func NotFound(message string) *Error
- func ServiceUnavailable(message string) *Error
- func TooManyRequests(message string) *Error
- func Unauthorized(message string) *Error
- func Unprocessable(message string) *Error
- type ErrorHandler
- type ErrorResponse
- type Event
- type EventBus
- func (bus *EventBus) Close(context.Context) error
- func (bus *EventBus) Emit[E event[D], D any](data D) *sync.WaitGroup
- func (bus *EventBus) Init(context.Context) error
- func (bus *EventBus) Name() string
- func (bus *EventBus) RemoveByService(service Service)
- func (bus *EventBus) Subscribe[E event[D], D any](handler func(D))
- func (bus *EventBus) SubscribeWithOwner[E event[D], D any](service Service, handler func(D))
- type FieldError
- type HealthProvider
- type HealthReporter
- type Job
- type JobQueueProvider
- type LogEvent
- type Logger
- type LoggerContext
- type Middleware
- type Migration
- type MigrationManager
- type NopLogEvent
- func (e *NopLogEvent) Any(string, any) LogEvent
- func (e *NopLogEvent) Bool(string, bool) LogEvent
- func (e *NopLogEvent) Duration(string, time.Duration) LogEvent
- func (e *NopLogEvent) Err(string, error) LogEvent
- func (e *NopLogEvent) Float64(string, float64) LogEvent
- func (e *NopLogEvent) Int(string, int) LogEvent
- func (e *NopLogEvent) Int64(string, int64) LogEvent
- func (e *NopLogEvent) Send()
- func (e *NopLogEvent) Str(string, string) LogEvent
- type NopLogger
- type NopLoggerContext
- func (c *NopLoggerContext) Any(string, any) LoggerContext
- func (c *NopLoggerContext) Bool(string, bool) LoggerContext
- func (c *NopLoggerContext) Duration(string, time.Duration) LoggerContext
- func (c *NopLoggerContext) Err(string, error) LoggerContext
- func (c *NopLoggerContext) Float64(string, float64) LoggerContext
- func (c *NopLoggerContext) Int(string, int) LoggerContext
- func (c *NopLoggerContext) Int64(string, int64) LoggerContext
- func (c *NopLoggerContext) Logger() Logger
- func (c *NopLoggerContext) Str(string, string) LoggerContext
- type Option
- type Principal
- type PrincipalMetadata
- type ReadyProvider
- type ReadyReporter
- type RegisteredRoute
- type RequestLoggerOption
- type RequestLoggerOptions
- type Resolver
- type Result
- type Router
- func (r *Router) Close(context.Context) error
- func (r *Router) Delete(service Service, path string, handler any, middleware ...Middleware)
- func (r *Router) Get(service Service, path string, handler any, middleware ...Middleware)
- func (r *Router) Group(fn func(sub *Router))
- func (r *Router) Handle(service Service, method, path string, handler any, middleware ...Middleware)
- func (r *Router) Head(service Service, path string, handler any, middleware ...Middleware)
- func (r *Router) Init(context.Context) error
- func (r *Router) Mux() chi.Router
- func (r *Router) Name() string
- func (r *Router) NamedMiddleware() map[string]string
- func (r *Router) NotFound(service Service, handler any)
- func (r *Router) Patch(service Service, path string, handler any, middleware ...Middleware)
- func (r *Router) Post(service Service, path string, handler any, middleware ...Middleware)
- func (r *Router) Put(service Service, path string, handler any, middleware ...Middleware)
- func (r *Router) Register(service Service, name string, mw func(http.Handler) http.Handler)
- func (r *Router) RemoveByService(service Service)
- func (r *Router) Route(pattern string, fn func(sub *Router))
- func (r *Router) Routes() map[string][]RegisteredRoute
- func (r *Router) Seal()
- func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
- func (r *Router) SetErrorHandler(h ErrorHandler)
- func (r *Router) Use(middleware ...Middleware)
- func (r *Router) UseMiddlewareByName(middleware string)
- func (r *Router) UseMiddlewareFunc(middleware func(http.Handler) http.Handler)
- type Rows
- type Scope
- type SecurityHeadersOption
- func WithSecurityHeadersContentSecurityPolicy(val string) SecurityHeadersOption
- func WithSecurityHeadersContentTypeOptions(val string) SecurityHeadersOption
- func WithSecurityHeadersCrossOriginOpenerPolicy(val string) SecurityHeadersOption
- func WithSecurityHeadersCrossOriginResourcePolicy(val string) SecurityHeadersOption
- func WithSecurityHeadersFrameOptions(val string) SecurityHeadersOption
- func WithSecurityHeadersPermissionsPolicy(val string) SecurityHeadersOption
- func WithSecurityHeadersReferrerPolicy(val string) SecurityHeadersOption
- func WithSecurityHeadersStrictTransportSecurity(val string) SecurityHeadersOption
- type SecurityHeadersOptions
- type ServerSettings
- type Service
- type ServiceContainer
- func (c *ServiceContainer) BuildAll(ctx context.Context) error
- func (c *ServiceContainer) CanResolve[T any]() bool
- func (c *ServiceContainer) EachInstance(fn func(reflect.Value))
- func (c *ServiceContainer) MustResolve[T any]() T
- func (c *ServiceContainer) NewScope(ctx context.Context) *Scope
- func (c *ServiceContainer) RegisterScopedService[T any](ctor any)
- func (c *ServiceContainer) RegisterService[T any](ctor any, lifetime ServiceLifetime)
- func (c *ServiceContainer) RegisterServiceInstance[T any](val T)
- func (c *ServiceContainer) RegisterSingletonService[T any](ctor any)
- func (c *ServiceContainer) RegisterTransientService[T any](ctor any)
- func (c *ServiceContainer) Resolve[T any]() (T, error)
- type ServiceContainerOption
- type ServiceContainerOptions
- type ServiceInitTimeout
- type ServiceLifetime
- type SystemAllServicesInitialized
- type SystemAllServicesInitializedPayload
- type SystemServerShuttingDown
- type SystemServerShuttingDownPayload
- type SystemServiceClosed
- type SystemServiceClosedPayload
- type SystemServiceInitialized
- type SystemServiceInitializedPayload
- type SystemShuttingDown
- type SystemShuttingDownPayload
- type TemplateProvider
- type TemplatedEmail
- type UIProvider
- type WithGasEnv
- type Worker
- func (w *Worker) ActiveServices() []string
- func (w *Worker) ActiveServicesMap() map[string]Service
- func (w *Worker) CheckHealth(ctx context.Context) map[string]error
- func (w *Worker) CheckReady(ctx context.Context) map[string]error
- func (w *Worker) CloseService[T Service](ctx context.Context) error
- func (w *Worker) ConfigProvider() ConfigProvider
- func (w *Worker) EventBus() *EventBus
- func (w *Worker) InitServices(ctx context.Context) error
- func (w *Worker) MigrationManager() MigrationManager
- func (w *Worker) ReadyFunc(fn func(*ServiceContainer) error)
- func (w *Worker) RegisterScopedService[T any](ctor any)
- func (w *Worker) RegisterService[T any](ctor any, lifetime ServiceLifetime)
- func (w *Worker) RegisterServiceInstance[T any](val T)
- func (w *Worker) RegisterSingletonService[T any](ctor any)
- func (w *Worker) RegisterTransientService[T any](ctor any)
- func (w *Worker) RestartService[T Service](ctx context.Context) error
- func (w *Worker) Run() error
- func (w *Worker) ServiceContainer() *ServiceContainer
- func (w *Worker) Shutdown(ctx context.Context) (err error)
- func (w *Worker) Start(ctx context.Context) error
- type WorkerOption
- func WithReadyFunc(fn func(*ServiceContainer) error) WorkerOption
- func WithScopedService[T any](ctor any) WorkerOption
- func WithService[T any](ctor any, lifetime ServiceLifetime) WorkerOption
- func WithServiceInitTimeout(timeout time.Duration) WorkerOption
- func WithServiceInstance[T any](val T) WorkerOption
- func WithSingletonService[T any](ctor any) WorkerOption
- func WithTransientService[T any](ctor any) WorkerOption
Constants ¶
const ( CodeBadRequest = "bad_request" CodeInvalidJSON = "invalid_json" CodeInvalidForm = "invalid_form" CodeForbidden = "forbidden" CodeNotFound = "not_found" CodeConflict = "conflict" CodeValidationFailed = "validation_failed" CodeRateLimited = "rate_limited" CodeInternal = "internal_error" CodePayloadTooLarge = "payload_too_large" )
Error codes written by Gas core. Applications are free to define their own and pass them to NewError.
Variables ¶
var ( // ErrNotRegistered reports that a type, or a dependency of one, has no // registration or instance in the container. ErrNotRegistered = errors.New("gas: service not registered") // ErrCircularDependency reports that the registered constructors depend // on each other in a cycle, so no build order exists. It is also returned // when a constructor or Init resolves the type being built through a // captured container or scope, when two goroutines' singleton builds // would wait on each other, and when constructors nest more than 256 deep. ErrCircularDependency = errors.New("gas: circular dependency") // ErrCaptiveDependency reports a singleton whose constructor depends on a // scoped or transient service, which the singleton would hold for the // life of the process. ErrCaptiveDependency = errors.New("gas: captive dependency") // ErrScopeRequired reports a scoped service resolved from the container // itself; it can only be resolved from a Scope. ErrScopeRequired = errors.New("gas: scoped service resolved outside a scope") // ErrNoConstructor reports that the container was asked to construct a // type it has no constructor for. It guards an internal invariant: every // public path checks the registration first, so the public API does not // return it today. ErrNoConstructor = errors.New("gas: no constructor") // ErrInvalidLifetime reports a registration whose ServiceLifetime is not // Singleton, Scoped, or Transient. ErrInvalidLifetime = errors.New("gas: invalid service lifetime") // ErrInvalidServiceShape reports a registered type that declares Init or // Close but does not implement Service. ErrInvalidServiceShape = errors.New("gas: invalid service shape") )
Sentinel errors for dependency resolution. Errors from the container wrap one of these, so callers can classify a failure with errors.Is while the message still names the types involved.
Functions ¶
func ApplyEnqueueOptions ¶
func ApplyEnqueueOptions( opts []EnqueueOption, ) (delay time.Duration, groupID, dedupeID string, attrs map[string]string)
ApplyEnqueueOptions resolves variadic EnqueueOption values into a concrete enqueueOptions struct. Implementations call this inside their Enqueue method.
func CacheControl ¶
func CacheControl(opt ...CacheControlOption) func(next http.Handler) http.Handler
CacheControl is middleware that sets the Cache-Control response header based on path matching rules and configured directives. If no path filters are specified, the header is applied to all requests. If no directives are specified, the middleware passes through without setting any header.
func MetadataValue ¶
func MetadataValue[T any](m PrincipalMetadata, key string) (T, bool)
MetadataValue is a type-safe helper that retrieves a typed value from PrincipalMetadata. It returns the value and true if the key exists and the type assertion succeeds, or the zero value and false otherwise.
func MustResolveFromRequestScope ¶
MustResolveFromRequestScope retrieves a service of type T from the request's Scope and panics if it cannot be resolved.
func NewNopLogger ¶
NewNopLogger returns a constructor that constructs a nop-logger implementing the Logger interface, in the CONVENTIONS.md shape. It never fails. Code that needs a Logger value directly, such as a test, can use &NopLogger{}.
func RequestLogger ¶
RequestLogger is middleware that logs HTTP requests and responses. It resolves T from the request's DI scope, derives a per-request logger from it with With, and stores that logger on the request context with WithLogger, so handlers and later middleware log through it with LoggerFromContext. The resolved logger itself is never changed, so a singleton shared by every request is safe.
When T is registered scoped (the usual pattern: a request-logger type built from the singleton gas.Logger with With), the per-request logger also replaces T's instance in the request scope, so handlers and services that take T as a dependency later in the request get it, request fields included. A singleton T is not shadowed, and a scoped T whose instance is a Service is not replaced, so the scope still closes it. When the request finishes it logs method, path, status, bytes written, duration, and remote address: at error level for a status >= 400, at info level otherwise.
The type parameter T must be a Logger type registered in the DI container. If it cannot be resolved, the middleware passes through silently.
When appendRequestID is enabled (the default), the per-request logger carries a request_id field, and the middleware expects chi's RequestID middleware to be mounted upstream so that a request ID is available via middleware.GetReqID.
func ResolveFromRequestScope ¶
ResolveFromRequestScope retrieves or builds a service of type T from the per-request scope in the provided *http.Request.
func SecurityHeaders ¶
func SecurityHeaders(opt ...SecurityHeadersOption) func(next http.Handler) http.Handler
SecurityHeaders is middleware that sets common security-related HTTP response headers. It applies secure defaults out of the box (nosniff, DENY framing, strict referrer policy, and restrictive permissions policy). Use the functional options to override individual header values, or pass an empty string to disable a specific header.
func WantsJSON ¶ added in v0.3.5
WantsJSON reports whether the client prefers a JSON error body. A request that explicitly asks for text/html without also accepting application/json gets plain text; everything else, including an absent or */* Accept header, gets JSON.
Quality values are ignored: the rule is media-type token presence only.
func WithLogger ¶
WithLogger returns a copy of ctx with the given Logger attached.
func WithPrincipal ¶
WithPrincipal stores a Principal in the given context.
func WithRequestScope ¶
WithRequestScope adds a Scope instance to the context using a custom key for managing scoped service lifetimes. Useful for testing and managing scoped service lifetimes within request contexts.
func WriteError ¶ added in v0.3.5
WriteError renders err as the unified error response, negotiating JSON or plain text via the Accept header. Values that are not, and do not wrap, an *Error render as a canonical 500 with the original discarded from the response.
WriteError does not log; the caller decides that. It reads only w and r, so it is safe to call from middleware that runs before the request scope exists.
Types ¶
type App ¶
type App struct {
*Worker
// contains filtered or unexported fields
}
App manages service lifecycle, the HTTP server, and graceful shutdown. It embeds a Worker for DI, events, migrations, and service management, and adds routing, CSRF protection, and an HTTP listener on top.
func NewApp ¶
NewApp creates an App with the given options. Router and EventBus are created internally and registered in the container.
func (*App) Handler ¶
Handler returns the fully wrapped http.Handler used by the App's server (router behind CSRF protection). Useful for embedding the App in tests or in an externally managed listener.
func (*App) Run ¶
Run initializes all services, runs pending migrations, starts the HTTP server, and blocks until a shutdown signal is received. Startup and shutdown use context.Background(). If the server fails, Run stops the App and returns the server error joined with any error from Stop.
func (*App) Serve ¶
Serve starts the HTTP server and blocks until it stops. A clean shutdown (http.ErrServerClosed) returns nil; any other listener error is returned.
func (*App) Server ¶
Server returns the App's *http.Server, constructing it on first call from the current Config. The instance is cached and safe to call concurrently.
func (*App) Start ¶
Start initializes the underlying Worker (services, migrations, hooks) and binds configuration. It does not start the HTTP server; call Serve for that, or use Run to do both and block until shutdown. ctx is passed to the Init of every service built during startup.
Like Worker.Start, a Start that fails, including when the ConfigProvider cannot bind the config (Bind also checks its validate tags), closes every service it had initialized and returns the error; every later Start returns the same error.
type AppConfigUpdated ¶
type AppConfigUpdated struct {
Event[AppConfigUpdatedPayload]
}
AppConfigUpdated is emitted when the app config is updated.
type AppConfigUpdatedPayload ¶
type AppConfigUpdatedPayload struct {
Config Config
}
AppConfigUpdatedPayload carries the updated config.
type AppOption ¶
type AppOption func(*App)
AppOption configures HTTP-specific aspects of an App (error handler, CSRF, trusted origins). Only NewApp accepts AppOption values; passing one to NewWorker panics.
func WithCSRFDenyHandler ¶
WithCSRFDenyHandler sets the handler invoked when a cross-origin request is rejected by CSRF protection. The default handler returns 403 Forbidden.
func WithCSRFInsecureBypassPattern ¶
WithCSRFInsecureBypassPattern adds a URL path pattern that bypasses CSRF cross-origin protection. Use only for endpoints that require unauthenticated cross-origin access and implement their own request validation (e.g. webhook receivers).
func WithErrorHandler ¶
func WithErrorHandler(h ErrorHandler) AppOption
WithErrorHandler configures the function that converts DI-aware handler errors into HTTP responses.
func WithTrustedOrigin ¶
WithTrustedOrigin adds an origin that is permitted to make cross-origin non-safe requests (POST, PUT, PATCH, DELETE, etc.). The origin must be an absolute URL with a scheme and host, e.g. "https://app.example.com". Panics if the origin is not a valid absolute URL.
type BasePrincipalMetadata ¶
BasePrincipalMetadata is a map-based implementation of PrincipalMetadata.
func (BasePrincipalMetadata) Value ¶
func (m BasePrincipalMetadata) Value(key string) any
Value returns the metadata value for the given key, or nil if not present.
type BindOption ¶ added in v0.5.5
type BindOption func(*BindOptions)
BindOption changes the BindOptions of one Bind call.
type BindOptions ¶ added in v0.5.5
type BindOptions struct {
// Validate runs struct-tag validation after binding. It defaults to true.
Validate bool
}
BindOptions holds the settings for one ConfigProvider.Bind call.
type CacheControlOption ¶
type CacheControlOption func(*CacheControlOptions)
CacheControlOption is a functional option for configuring CacheControl.
func WithCacheControlImmutable ¶
func WithCacheControlImmutable() CacheControlOption
WithCacheControlImmutable appends the "immutable" directive.
func WithCacheControlMaxAge ¶
func WithCacheControlMaxAge(val time.Duration) CacheControlOption
WithCacheControlMaxAge appends a "max-age" directive with the given duration.
func WithCacheControlMustRevalidate ¶
func WithCacheControlMustRevalidate() CacheControlOption
WithCacheControlMustRevalidate appends the "must-revalidate" directive.
func WithCacheControlMustUnderstand ¶
func WithCacheControlMustUnderstand() CacheControlOption
WithCacheControlMustUnderstand appends the "must-understand" directive.
func WithCacheControlNoCache ¶
func WithCacheControlNoCache() CacheControlOption
WithCacheControlNoCache appends the "no-cache" directive.
func WithCacheControlNoStore ¶
func WithCacheControlNoStore() CacheControlOption
WithCacheControlNoStore appends the "no-store" directive.
func WithCacheControlNoTransform ¶
func WithCacheControlNoTransform() CacheControlOption
WithCacheControlNoTransform appends the "no-transform" directive.
func WithCacheControlPath ¶
func WithCacheControlPath(val string) CacheControlOption
WithCacheControlPath adds an exact path that should receive the Cache-Control header.
func WithCacheControlPathPrefix ¶
func WithCacheControlPathPrefix(val string) CacheControlOption
WithCacheControlPathPrefix adds a path prefix to match against. Any request whose path starts with this prefix will receive the Cache-Control header.
func WithCacheControlPathPrefixes ¶
func WithCacheControlPathPrefixes(val []string) CacheControlOption
WithCacheControlPathPrefixes adds multiple path prefixes to match against.
func WithCacheControlPathSuffix ¶
func WithCacheControlPathSuffix(val string) CacheControlOption
WithCacheControlPathSuffix adds a path suffix to match against (e.g., ".css", ".js"). Any request whose path ends with this suffix will receive the Cache-Control header.
func WithCacheControlPathSuffixes ¶
func WithCacheControlPathSuffixes(val []string) CacheControlOption
WithCacheControlPathSuffixes adds multiple path suffixes to match against.
func WithCacheControlPaths ¶
func WithCacheControlPaths(val []string) CacheControlOption
WithCacheControlPaths adds multiple exact paths that should receive the Cache-Control header.
func WithCacheControlPrivate ¶
func WithCacheControlPrivate() CacheControlOption
WithCacheControlPrivate appends the "private" directive.
func WithCacheControlProxyRevalidate ¶
func WithCacheControlProxyRevalidate() CacheControlOption
WithCacheControlProxyRevalidate appends the "proxy-revalidate" directive.
func WithCacheControlPublic ¶
func WithCacheControlPublic() CacheControlOption
WithCacheControlPublic appends the "public" directive.
func WithCacheControlSMaxAge ¶
func WithCacheControlSMaxAge(val time.Duration) CacheControlOption
WithCacheControlSMaxAge appends an "s-maxage" directive (shared/CDN cache max age).
func WithCacheControlStaleIfError ¶
func WithCacheControlStaleIfError(val time.Duration) CacheControlOption
WithCacheControlStaleIfError appends a "stale-if-error" directive.
func WithCacheControlStaleWhileRevalidate ¶
func WithCacheControlStaleWhileRevalidate(val time.Duration) CacheControlOption
WithCacheControlStaleWhileRevalidate appends a "stale-while-revalidate" directive.
type CacheControlOptions ¶
type CacheControlOptions struct {
// contains filtered or unexported fields
}
CacheControlOptions configures the behavior of the CacheControl middleware.
type CacheProvider ¶
type CacheProvider interface {
Get(ctx context.Context, key string) ([]byte, error)
Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
Delete(ctx context.Context, key string) error
Exists(ctx context.Context, key string) (bool, error)
}
CacheProvider abstracts key-value caching. Implemented by in-memory, Redis, Valkey, or any other cache service.
type Config ¶
type Config struct {
// Embedded GasEnv
WithGasEnv
Server ServerSettings
}
Config holds server-level configuration passed from the host server to the App. Sensible defaults are applied via DefaultConfig().
func DefaultConfig ¶
func DefaultConfig() *Config
DefaultConfig returns a Config with sensible defaults.
type ConfigProvider ¶
type ConfigProvider interface {
SetDefault(key string, value any)
SetDefaults(values any) error
Set(key string, value any)
// Bind fills dest from the configuration and then validates it against
// its validate struct tags, unless an option turns validation off.
Bind(dest any, options ...BindOption) error
Get(key string) any
Find(key string) (value any, exist bool)
Values() map[string]any
}
ConfigProvider is the configuration API services depend on: reading values, setting defaults, and binding into a struct. The config module's *Config implements it. Loading sources is setup work the app does on the concrete config before registering it, so it is not part of this interface.
type Context ¶
type Context interface {
context.Context
// ResponseWriter returns the underlying http.ResponseWriter.
ResponseWriter() http.ResponseWriter
// Request returns the underlying *http.Request.
Request() *http.Request
// JSON serializes v as JSON and writes it with the given status code.
JSON(status int, v any) error
// XML serializes v as XML and writes it with the given status code.
// Uses content type "application/xml; charset=utf-8".
XML(status int, v any) error
// RSS serializes v as XML with content type "application/rss+xml; charset=utf-8".
RSS(status int, v any) error
// HTML writes an HTML response with the given status code and content string.
HTML(status int, s string) error
// Text writes a plain-text response with the given status code.
Text(status int, s string) error
// NoContent writes a 204 No Content response.
NoContent() error
// WriteError writes err as the unified error response, negotiating JSON or
// plain text the same way the default ErrorHandler does. Values that are
// not, and do not wrap, an *Error render as a canonical 500.
WriteError(err error) error
// WriteErrorJSON writes the JSON error envelope regardless of the Accept
// header. Use it for JSON API routes in an app whose ErrorHandler renders
// HTML.
WriteErrorJSON(err error) error
// BadRequest returns a 400 *Error with code CodeBadRequest.
BadRequest(msg string) error
Unauthorized(msg string) error
// Forbidden returns a 403 *Error with code CodeForbidden.
Forbidden(msg string) error
// NotFound returns a 404 *Error with code CodeNotFound.
NotFound(msg string) error
// Conflict returns a 409 *Error with code CodeConflict.
Conflict(msg string) error
// Unprocessable returns a 422 *Error with code CodeValidationFailed.
Unprocessable(msg string) error
// TooManyRequests returns a 429 *Error with code CodeRateLimited.
TooManyRequests(msg string) error
// Internal returns a 500 *Error with code CodeInternal.
Internal(msg string) error
ServiceUnavailable(msg string) error
// BadRequestErr returns a 400 *Error with code CodeBadRequest, caused by err.
BadRequestErr(err error, msg string) error
UnauthorizedErr(err error, msg string) error
// ForbiddenErr returns a 403 *Error with code CodeForbidden, caused by err.
ForbiddenErr(err error, msg string) error
// NotFoundErr returns a 404 *Error with code CodeNotFound, caused by err.
NotFoundErr(err error, msg string) error
// ConflictErr returns a 409 *Error with code CodeConflict, caused by err.
ConflictErr(err error, msg string) error
// UnprocessableErr returns a 422 *Error with code CodeValidationFailed,
// caused by err.
UnprocessableErr(err error, msg string) error
// TooManyRequestsErr returns a 429 *Error with code CodeRateLimited,
// caused by err.
TooManyRequestsErr(err error, msg string) error
// InternalErr returns a 500 *Error with code CodeInternal, caused by err.
InternalErr(err error, msg string) error
// caused by err.
ServiceUnavailableErr(err error, msg string) error
// Redirect sends an HTTP redirect to the given URL with the given status code.
Redirect(status int, url string)
// Param returns the URL parameter value by name (chi.URLParam).
Param(key string) string
// Query returns the query string parameter value by name.
Query(key string) string
// Header returns the request header value by name.
Header(key string) string
// SetHeader sets a response header.
SetHeader(key, value string)
// BindJSON decodes the request body as JSON into dest and performs automatic validation
// using the configured validator.
BindJSON(dest any) error
// BindForm binds form data from the HTTP request to the provided destination object
// and performs automatic validation using the configured validator.
BindForm(dest any) error
// Validator returns the *validator.Validate used for request validation,
// building the package default on first use if none was supplied.
Validator() *validator.Validate
// FormDecoder returns the *schema.Decoder used to decode form data into
// structs, building the package default on first use if none was supplied.
FormDecoder() *schema.Decoder
// Principal returns the authenticated Principal attached to the request,
// or a 401 *Error with code CodeUnauthorized when none is present.
Principal() (Principal, error)
}
Context is the first parameter of every DI-aware handler. It wraps the HTTP response writer and request into a single value. The per-request scope is accessible via RequestScope(c.Request()) — the adapter resolves dependencies automatically, so handlers rarely need to access the scope directly.
func NewContext ¶
func NewContext( parent context.Context, w http.ResponseWriter, r *http.Request, opts ...ContextOption, ) Context
NewContext creates a Context from the standard HTTP pair. It panics if parent, w, or r is nil.
The returned Context is installed as the request's own context, so Request().Context() and the Context itself are the same value.
type ContextOption ¶
type ContextOption func(*reqContext)
ContextOption is a functional option used to modify or extend the behavior of a reqContext at creation time.
func WithFormDecoder ¶
func WithFormDecoder(d *schema.Decoder) ContextOption
WithFormDecoder sets a custom form decoder for the reqContext using the provided *schema.Decoder instance.
func WithMaxJSONBodyBytes ¶ added in v0.5.5
func WithMaxJSONBodyBytes(n int64) ContextOption
WithMaxJSONBodyBytes sets how many bytes of the request body BindJSON reads before it fails with a 413 *Error. Zero or a negative n means no limit. Without this option the limit is 1 MiB.
func WithValidate ¶
func WithValidate(v *validator.Validate) ContextOption
WithValidate returns a ContextOption that sets the provided *validator.Validate instance to the reqContext.
type DatabaseProvider ¶
type DatabaseProvider interface {
DB() *sql.DB
Driver() string
Ping(ctx context.Context) error
Query(ctx context.Context, query string, args ...any) (Rows, error)
Exec(ctx context.Context, query string, args ...any) (Result, error)
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
WithTx(ctx context.Context, opts *sql.TxOptions, fn func(*sql.Tx) error) (err error)
}
DatabaseProvider abstracts database access. Implemented by gas/database or any other database service. DB() exposes the underlying *sql.DB so that sqlc-generated code and transactions can use it directly.
type Email ¶
type Email struct {
From string
ReplyTo string
Subject string
TextBody string
HTMLBody string
Headers map[string]string
To []string
Cc []string
Bcc []string
}
Email represents an email message.
type EmailProvider ¶
type EmailProvider interface {
Send(ctx context.Context, msg *Email) error
SendFromTemplate(ctx context.Context, msg *TemplatedEmail) error
}
EmailProvider abstracts email sending.
type EnqueueOption ¶
type EnqueueOption func(*enqueueOptions)
EnqueueOption configures an Enqueue call.
func WithDedupeID ¶
func WithDedupeID(id string) EnqueueOption
WithDedupeID sets a deduplication identifier.
func WithDelay ¶
func WithDelay(d time.Duration) EnqueueOption
WithDelay sets an initial delay before the job becomes visible to consumers.
func WithGroupID ¶
func WithGroupID(id string) EnqueueOption
WithGroupID sets the message group for FIFO queue ordering.
func WithJobAttributes ¶
func WithJobAttributes(attrs map[string]string) EnqueueOption
WithJobAttributes attaches provider-specific key-value metadata to the job.
type Environment ¶ added in v0.5.5
type Environment string
Environment is an application environment such as development or production. Values are case-sensitive. The config module's gasenv extension resolves it at load time and stores it under the GasEnv key, so a configuration struct that embeds WithGasEnv gets it when bound:
type AppConfig struct {
gas.WithGasEnv
Database DatabaseConfig
}
var c AppConfig
if err := cfg.Bind(&c); err != nil {
return err
}
if c.GasEnv.IsProduction() {
// production-only setup
}
const ( // EnvDevelopment is local development, typically with debug logging and // development tools. It is the gasenv extension's fallback. EnvDevelopment Environment = "development" // EnvTesting is automated and integration testing. EnvTesting Environment = "testing" // EnvStaging is a production-like environment for validation before a // release. EnvStaging Environment = "staging" // EnvProduction serves real users. EnvProduction Environment = "production" )
The environments the gasenv extension allows by default.
func (Environment) IsDevelopment ¶ added in v0.5.5
func (e Environment) IsDevelopment() bool
IsDevelopment reports whether e is EnvDevelopment.
func (Environment) IsDevelopmentLike ¶ added in v0.5.5
func (e Environment) IsDevelopmentLike() bool
IsDevelopmentLike reports whether e is EnvDevelopment or EnvTesting.
func (Environment) IsProduction ¶ added in v0.5.5
func (e Environment) IsProduction() bool
IsProduction reports whether e is EnvProduction.
func (Environment) IsProductionLike ¶ added in v0.5.5
func (e Environment) IsProductionLike() bool
IsProductionLike reports whether e is EnvProduction or EnvStaging.
func (Environment) IsStaging ¶ added in v0.5.5
func (e Environment) IsStaging() bool
IsStaging reports whether e is EnvStaging.
func (Environment) IsTesting ¶ added in v0.5.5
func (e Environment) IsTesting() bool
IsTesting reports whether e is EnvTesting.
func (Environment) String ¶ added in v0.5.5
func (e Environment) String() string
String returns e as a plain string.
type Error ¶ added in v0.3.5
type Error struct {
Status int `json:"status"`
Code string `json:"code"`
Message string `json:"message"`
Fields []FieldError `json:"fields,omitempty"`
Details map[string]any `json:"details,omitempty"`
// contains filtered or unexported fields
}
Error is the unified error shape for Gas handlers. Return it from a DI-aware handler and the ErrorHandler renders it; see WriteError for the wire format.
Status is serialized into the JSON body alongside the HTTP status line. WriteError writes the normalized status to both, so the two never disagree.
The wrapped cause is unexported and never serialized. It reaches logs and errors.Is / errors.As, never a client.
Field order is the JSON key order of the response body, so it is fixed by the wire contract rather than by memory layout.
func BadRequest ¶ added in v0.3.5
BadRequest returns a 400 Error with code CodeBadRequest.
func NewError ¶ added in v0.3.5
NewError builds an Error with an explicit status, code, and message.
func ServiceUnavailable ¶ added in v0.3.5
ServiceUnavailable returns a 503 Error with code CodeUnavailable.
func TooManyRequests ¶ added in v0.3.5
TooManyRequests returns a 429 Error with code CodeRateLimited.
func Unauthorized ¶ added in v0.3.5
Unauthorized returns a 401 Error with code CodeUnauthorized.
func Unprocessable ¶ added in v0.3.5
Unprocessable returns a 422 Error with code CodeValidationFailed.
func (*Error) Error ¶ added in v0.3.5
Error implements the error interface. The wrapped cause is included so that logs carry the full chain.
func (*Error) Unwrap ¶ added in v0.3.5
Unwrap returns the wrapped cause, so errors.Is and errors.As see through it.
func (*Error) WithCause ¶ added in v0.3.5
WithCause attaches an underlying cause. The cause is logged and is reachable through errors.Is and errors.As, but is never written to a response.
func (*Error) WithDetail ¶ added in v0.3.5
WithDetail attaches an arbitrary key/value pair to the response payload.
type ErrorHandler ¶
ErrorHandler converts a handler error into an HTTP response.
type ErrorResponse ¶ added in v0.3.5
type ErrorResponse struct {
Error *Error `json:"error"`
}
ErrorResponse is the wire shape of every error response Gas writes. It is exported so Go clients and tests can decode it.
type Event ¶
type Event[D any] struct{}
Event is embedded in an event type to declare the payload that event carries. An event is a type, not a value:
type OrderPlaced struct{ gas.Event[OrderPlacedPayload] }
Emit, Subscribe, and SubscribeWithOwner take that type as their only explicit type argument and infer the payload from it, so a handler registered for OrderPlaced is bound at compile time to OrderPlacedPayload. Subscriptions are keyed by the event type itself, so two events never collide, even when their payload types are identical.
type EventBus ¶
type EventBus struct {
// contains filtered or unexported fields
}
EventBus is a publish/subscribe message bus with service ownership tracking. Subscriptions are keyed by event type; those registered through SubscribeWithOwner are additionally tagged with the owning service, so the kill switch can drop them as a group when that service closes.
All methods are safe for concurrent use.
func (*EventBus) Close ¶ added in v0.5.0
Close is a no-op; subscriptions are removed through RemoveByService.
func (*EventBus) Emit ¶
Emit dispatches data to every subscriber of event type E, each on its own goroutine, and returns a WaitGroup that completes once they all return:
bus.Emit[OrderPlaced](OrderPlacedPayload{ID: id}).Wait()
Wait on it when handlers must finish before the caller continues. The returned WaitGroup is never nil, and emitting an event nobody subscribes to is a no-op. Emit holds the bus lock only long enough to snapshot the subscriber list; handlers run without it, so a handler may subscribe or emit in turn.
A handler that panics is recovered, so it neither takes the process down nor keeps the WaitGroup from completing, and the other handlers still run. The stack goes to stderr and, on a Worker's bus, the panic is logged with the event type and the owning service.
func (*EventBus) Init ¶ added in v0.5.0
Init is a no-op. It exists so the EventBus satisfies Service and can be passed as an owner.
func (*EventBus) RemoveByService ¶ added in v0.3.5
RemoveByService removes every subscription that service registered through SubscribeWithOwner, across all event types, and drops any event whose last subscriber is gone. Subscriptions made with Subscribe have no owner and are left in place.
func (*EventBus) Subscribe ¶
Subscribe registers a handler for event type E with no owning service:
bus.Subscribe[OrderPlaced](func(p OrderPlacedPayload) { ... })
Use SubscribeWithOwner instead when subscribing from a service, so RemoveByService can clean the subscription up when that service closes. Handlers are started in registration order but run concurrently, so a handler that touches shared state must do its own synchronization.
func (*EventBus) SubscribeWithOwner ¶
SubscribeWithOwner registers a handler for event type E and records service as its owner. Worker.CloseService uses that ownership to remove every subscription belonging to a service it tears down, so services should always subscribe through this method rather than Subscribe.
type FieldError ¶ added in v0.3.5
type FieldError struct {
Field string `json:"field"`
Rule string `json:"rule"`
Message string `json:"message"`
}
FieldError describes a single field-level validation failure. Field carries the name the client sent (the json tag), not the Go struct field name.
type HealthProvider ¶
HealthProvider aggregates health signals across all active services that implement HealthReporter. The returned map keys are service names; a nil value indicates a healthy service.
type HealthReporter ¶
HealthReporter is implemented by services that can report their own health. Answers the question "am I alive?" — a failure here signals that the process is in a broken state that a restart would resolve (lost connection that won't recover, corrupt internal state, deadlock). Maps to a Kubernetes livenessProbe. A nil return means healthy; a non-nil error describes the failure.
type Job ¶
type Job struct {
ID string
ReceiptHandle string // opaque token used by Ack/Nack
Attributes map[string]string // provider-specific metadata
Body []byte
}
Job represents a message received from a queue.
type JobQueueProvider ¶
type JobQueueProvider interface {
Enqueue(ctx context.Context, queue string, payload []byte, opts ...EnqueueOption) error
Dequeue(ctx context.Context, queue string, maxMessages int, wait time.Duration) ([]Job, error)
Ack(ctx context.Context, queue string, job Job) error
Nack(ctx context.Context, queue string, job Job) error
}
JobQueueProvider abstracts async job/message queue processing. Implemented by gas/queue/sqs or any other queue service. The interface is pull-based: consumers call Dequeue in their own worker loop and acknowledge results with Ack/Nack.
type LogEvent ¶
type LogEvent interface {
Str(key, val string) LogEvent
Int(key string, val int) LogEvent
Int64(key string, val int64) LogEvent
Float64(key string, val float64) LogEvent
Bool(key string, val bool) LogEvent
Err(key string, val error) LogEvent
Duration(key string, val time.Duration) LogEvent
Any(key string, val any) LogEvent
// Send finalizes and emits the log event.
Send()
}
LogEvent is a single structured log entry. Field methods return the same LogEvent for chaining. Call Send to finalize and emit the event.
type Logger ¶
type Logger interface {
// Trace starts a log event at the TRACE level.
Trace(msg string) LogEvent
// Debug starts a log event at the DEBUG level.
Debug(msg string) LogEvent
// Info starts a log event at the INFO level.
Info(msg string) LogEvent
// Warn starts a log event at the WARN level.
Warn(msg string) LogEvent
// Error starts a log event at the ERROR level.
Error(msg string) LogEvent
// With returns a LoggerContext for building a sub-logger with persistent fields.
With() LoggerContext
// Flush writes any buffered log entries to the underlying output.
Flush()
}
Logger is the provider interface for structured logging in the Gas ecosystem. Implementations produce LogEvent values at a given severity level. Each method returns a LogEvent that can be enriched with typed fields before calling Send.
Use With to derive a sub-logger that carries persistent fields across all subsequent log events.
Call Flush to ensure all buffered log entries are written (e.g. before shutdown).
func LoggerFromContext ¶
LoggerFromContext returns the Logger stored in ctx, or nil if none is present.
type LoggerContext ¶
type LoggerContext interface {
Str(key, val string) LoggerContext
Int(key string, val int) LoggerContext
Int64(key string, val int64) LoggerContext
Float64(key string, val float64) LoggerContext
Bool(key string, val bool) LoggerContext
Err(key string, val error) LoggerContext
Duration(key string, val time.Duration) LoggerContext
Any(key string, val any) LoggerContext
// Logger returns a new Logger that includes all fields added to this context.
Logger() Logger
}
LoggerContext is a builder for deriving a sub-logger with persistent fields. Each field method returns the same LoggerContext for chaining. Call Logger to obtain the resulting Logger that carries all accumulated fields.
type Middleware ¶
type Middleware struct {
// contains filtered or unexported fields
}
Middleware represents either a named middleware (resolved from the router's registry at apply time) or an inline middleware function.
func MiddlewareByName ¶
func MiddlewareByName(name string) Middleware
MiddlewareByName creates a Middleware that will be resolved by name from the router's internal registry when applied.
func MiddlewareFunc ¶
func MiddlewareFunc(fn func(http.Handler) http.Handler) Middleware
MiddlewareFunc creates a Middleware that wraps an inline handler function directly. The middleware will be anonymous and omitted from the route map. Use MiddlewareFuncWithName to give it a display name.
func MiddlewareFuncWithName ¶
MiddlewareFuncWithName creates a named inline Middleware. The name appears in the route map alongside routes that this middleware applies to.
type MigrationManager ¶
type MigrationManager interface {
Service
// Register adds a migration and tracks which service owns it.
Register(service Service, m Migration)
// RegisterSlice adds multiple migrations at once for the given service.
RegisterSlice(service Service, migrations []Migration)
// RegisterFS reads .up.sql/.down.sql files from an fs.FS and registers
// them as migrations for the given service. Files must follow the naming
// convention: {version}_{description}.up.sql / {version}_{description}.down.sql
// (e.g. 20250216_001_create_users.up.sql).
RegisterFS(service Service, fsys fs.FS) error
// RunPending applies all unapplied migrations in global version order.
// If any migration is marked dirty, execution is blocked until the
// dirty state is manually resolved.
RunPending() error
// Down reverses the last n applied migrations in reverse version order.
Down(n int) error
}
MigrationManager is the interface for registering and executing database migrations. Services call Register during Init to declare their migrations. The implementation lives in a separate service (gas/migrate) and is wired in by the App.
type NopLogEvent ¶
type NopLogEvent struct{}
NopLogEvent is a LogEvent that discards all fields and performs no action on Send. All methods return the receiver for chaining.
func (*NopLogEvent) Any ¶
func (e *NopLogEvent) Any(string, any) LogEvent
Any adds a key-value pair to the event, where the value can be of any type, returning the event for chaining.
func (*NopLogEvent) Bool ¶
func (e *NopLogEvent) Bool(string, bool) LogEvent
Bool adds a boolean field to the log event and returns the event for method chaining.
func (*NopLogEvent) Duration ¶
func (e *NopLogEvent) Duration(string, time.Duration) LogEvent
Duration adds a field with a duration value to the log entry, identified by the given key.
func (*NopLogEvent) Err ¶
func (e *NopLogEvent) Err(string, error) LogEvent
Err adds an error field to the log event with the specified key and value. Returns the log event for chaining.
func (*NopLogEvent) Float64 ¶
func (e *NopLogEvent) Float64(string, float64) LogEvent
Float64 sets a float64 value for the specified key in the log event. It returns the LogEvent instance for chaining.
func (*NopLogEvent) Int ¶
func (e *NopLogEvent) Int(string, int) LogEvent
Int adds an integer field to the log event with the provided key and value, returning the LogEvent for chaining.
func (*NopLogEvent) Int64 ¶
func (e *NopLogEvent) Int64(string, int64) LogEvent
Int64 adds an int64 field to the log event with the specified key and value. It returns the LogEvent for chaining.
func (*NopLogEvent) Send ¶
func (e *NopLogEvent) Send()
Send finalizes the log event. In NopLogEvent, it performs no action and discards the event.
type NopLogger ¶
type NopLogger struct{}
NopLogger is a Logger implementation that silently discards all log output. It uses a singleton pattern — all instances share the same underlying value, resulting in zero allocations per log call.
func (*NopLogger) Debug ¶
Debug logs a debug-level message and returns a no-op LogEvent for chaining.
func (*NopLogger) Error ¶
Error logs an error-level message with no operational effect, returning a no-op LogEvent.
func (*NopLogger) Flush ¶
func (l *NopLogger) Flush()
Flush performs no operation as NopLogger discards all log output without buffering.
func (*NopLogger) Trace ¶
Trace creates a no-op log event for a trace-level message and silently discards it.
func (*NopLogger) With ¶
func (l *NopLogger) With() LoggerContext
With creates a new LoggerContext for deriving a sub-logger with persistent fields across log events.
type NopLoggerContext ¶
type NopLoggerContext struct{}
NopLoggerContext is a LoggerContext that discards all fields. All methods return the receiver for chaining and Logger returns a NopLogger.
func (*NopLoggerContext) Any ¶
func (c *NopLoggerContext) Any(string, any) LoggerContext
Any adds a field with the given key and value to the context, supporting any type, and returns the LoggerContext.
func (*NopLoggerContext) Bool ¶
func (c *NopLoggerContext) Bool(string, bool) LoggerContext
Bool adds a boolean field to the logging context under the specified key for structured logging.
func (*NopLoggerContext) Duration ¶
func (c *NopLoggerContext) Duration(string, time.Duration) LoggerContext
Duration adds a time.Duration field with the specified key and value to the logging context and returns it.
func (*NopLoggerContext) Err ¶
func (c *NopLoggerContext) Err(string, error) LoggerContext
Err adds an error field to the logger context with the specified key and error value, returning the same context.
func (*NopLoggerContext) Float64 ¶
func (c *NopLoggerContext) Float64(string, float64) LoggerContext
Float64 adds a float64 field with the specified key and value to the context and returns the LoggerContext for chaining.
func (*NopLoggerContext) Int ¶
func (c *NopLoggerContext) Int(string, int) LoggerContext
Int adds an integer field to the logger context with the specified key. Returns the same LoggerContext for chaining.
func (*NopLoggerContext) Int64 ¶
func (c *NopLoggerContext) Int64(string, int64) LoggerContext
Int64 adds a key-value pair with an int64 value to the log context and returns the updated LoggerContext.
func (*NopLoggerContext) Logger ¶
func (c *NopLoggerContext) Logger() Logger
Logger returns a no-op Logger instance that discards all log events.
func (*NopLoggerContext) Str ¶
func (c *NopLoggerContext) Str(string, string) LoggerContext
Str adds a string key-value pair to the logger context and returns the same context for chaining.
type Option ¶
type Option interface {
// contains filtered or unexported methods
}
Option is a marker interface satisfied by both WorkerOption and AppOption. NewWorker and NewApp accept ...Option so callers can pass either type.
type Principal ¶
type Principal interface {
Subject() string
Scheme() string
CredentialID() string
Metadata() PrincipalMetadata
}
Principal represents an authenticated identity. Subject is the stable user identifier, Scheme identifies the authentication method (e.g. "jwt", "session", "apikey"), and CredentialID is the specific credential instance (session ID, JWT jti, API key ID).
func PrincipalFromContext ¶
PrincipalFromContext retrieves the Principal from the context and reports whether one is present. A nil Principal, or a typed-nil one (a nil pointer of a type implementing Principal), counts as absent: the result is nil and false, so a caller checking ok never gets a principal it cannot use.
type PrincipalMetadata ¶
PrincipalMetadata provides read-only access to arbitrary key-value metadata attached to a Principal at construction time.
type ReadyProvider ¶
ReadyProvider aggregates readiness signals across all active services that implement ReadyReporter. The returned map keys are service names; a nil value indicates a ready service.
type ReadyReporter ¶
ReadyReporter is implemented by services that can report whether they are ready to accept traffic. Answers the question "should traffic be routed to me?" — distinct from health, since a service can be alive but not yet ready (warming caches, pending migrations, dependency briefly unavailable, draining during shutdown). Maps to a Kubernetes readinessProbe. A nil return means ready; a non-nil error describes why the service is not ready.
type RegisteredRoute ¶
RegisteredRoute is an exported snapshot of a registered route.
type RequestLoggerOption ¶
type RequestLoggerOption func(*RequestLoggerOptions)
RequestLoggerOption is a functional option for configuring RequestLogger.
func WithRequestLoggerAppendRequestID ¶
func WithRequestLoggerAppendRequestID(val bool) RequestLoggerOption
WithRequestLoggerAppendRequestID controls whether the request ID (from chi's RequestID middleware) is added to the logger's base fields. Defaults to true.
func WithRequestLoggerBuilder ¶
func WithRequestLoggerBuilder(fn func(Logger, *http.Request) Logger) RequestLoggerOption
WithRequestLoggerBuilder sets a function that customizes the logger used for each request. The builder receives the resolved Logger and the current request, and returns a (possibly wrapped or enriched) Logger. It is called after the request ID field is appended (if enabled). This can be used to add extra fields, swap the logger, or adjust log levels per request.
type RequestLoggerOptions ¶
type RequestLoggerOptions struct {
// contains filtered or unexported fields
}
RequestLoggerOptions configures the behavior of the RequestLogger middleware.
type Resolver ¶
type Resolver interface {
// contains filtered or unexported methods
}
Resolver is implemented by ServiceContainer and Scope to provide dependency resolution. The unexported method restricts implementation to this package.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router is a smart router that wraps Chi and tracks route and middleware ownership by service. MiddlewareByName middleware is validated against an internal registry at registration time and resolved on every build of the routing tree, so ownership changes reach chains registered earlier. The base server uses RemoveByService during kill-switch to replace everything a closed service registered — its routes and its named middleware — with 503.
Top-level routers created via NewRouter() start unsealed: Use, Handle, Group, and Route calls are deferred until Seal() is called. This lets services register middleware and routes in any order during Init. Seal() flushes all pending middleware first (via chi.Use), then replays pending route operations — guaranteeing middleware-before-routes ordering that Chi requires.
Sub-routers (created inside Group/Route callbacks) are always sealed so their calls pass through to Chi immediately.
Concurrency model: once sealed, the live chi tree is immutable. ServeHTTP reads it lock-free via an atomic pointer (served). Runtime mutators (RemoveByService, and Handle/Route/Group/Use via RestartService) never touch the tree in flight — they rebuild a fresh chi tree under r.mu by replaying the recorded registrations and atomically swap it in. In-flight requests keep serving the previous tree, which is never written after publication.
func NewRouter ¶
func NewRouter() *Router
NewRouter creates a Router backed by Chi with an empty middleware registry. The router starts unsealed — Use/Handle/Group/Route calls are deferred until Seal() is called.
func (*Router) Close ¶ added in v0.5.0
Close is a no-op; routes are removed through RemoveByService.
func (*Router) Delete ¶ added in v0.5.2
func (r *Router) Delete(service Service, path string, handler any, middleware ...Middleware)
Delete registers a DELETE route via Handle.
func (*Router) Get ¶ added in v0.5.2
func (r *Router) Get(service Service, path string, handler any, middleware ...Middleware)
Get registers a GET route via Handle, which also serves HEAD on the same path.
func (*Router) Group ¶
Group creates an inline route group. The callback receives a sub-Router that shares the parent's middleware registry and route tracking. When the router is unsealed, the group is deferred until Seal().
func (*Router) Handle ¶
func (r *Router) Handle( service Service, method, path string, handler any, middleware ...Middleware, )
Handle registers a route and tracks ownership. The handler can be:
- http.HandlerFunc or func(http.ResponseWriter, *http.Request) — passed through directly
- A DI-aware function: func(gas.Context, Dep1, Dep2, ...) error — dependencies are auto-resolved from the per-request scope at call time
Middleware is resolved from the internal registry (for MiddlewareByName) or used directly (for MiddlewareFunc) and applied in order (outermost first). Panics if a named middleware is not registered or if a DI-aware handler has an invalid signature. When the router is unsealed, the registration is deferred until Seal().
func (*Router) Head ¶ added in v0.5.2
func (r *Router) Head(service Service, path string, handler any, middleware ...Middleware)
Head registers a HEAD route via Handle.
func (*Router) Init ¶ added in v0.5.0
Init is a no-op. It exists so the Router satisfies Service and can be passed as an owner.
func (*Router) Mux ¶
Mux returns the underlying Chi router so the base server can add global middleware, mount sub-routers, or pass it to http.Server.
This returns the most recently built tree. Mutating it directly bypasses the router's synchronization and is only safe before serving begins.
func (*Router) NamedMiddleware ¶
NamedMiddleware returns a snapshot of the named middleware registry as a map of middleware name to owning service.
func (*Router) NotFound ¶
NotFound registers a custom not-found handler for the router, associated with the specified service. The handler can be http.HandlerFunc or a DI-aware function (same rules as Handle). Panics if a not found handler is already registered by another service.
func (*Router) Patch ¶ added in v0.5.2
func (r *Router) Patch(service Service, path string, handler any, middleware ...Middleware)
Patch registers a PATCH route via Handle.
func (*Router) Post ¶ added in v0.5.2
func (r *Router) Post(service Service, path string, handler any, middleware ...Middleware)
Post registers a POST route via Handle.
func (*Router) Put ¶ added in v0.5.2
func (r *Router) Put(service Service, path string, handler any, middleware ...Middleware)
Put registers a PUT route via Handle.
func (*Router) Register ¶
Register adds a named middleware to the internal registry and tracks which service owns it.
func (*Router) RemoveByService ¶ added in v0.3.5
RemoveByService tears down everything the given service registered. Its routes are replaced with 503 Service Unavailable handlers, and every named middleware it registered is replaced with a 503 short-circuit wherever it is referenced — including on routes owned by services that are still running, and including references nested inside Group/Route blocks. A route guarded by a killed service's middleware therefore stops serving: the middleware is disabled, never skipped, so a teardown can never drop an authorization check and leave the route open.
Register-ing the name again (the RestartService -> Init path) re-arms it.
The teardown is applied by rebuilding a fresh routing tree and swapping it in atomically, so requests in flight on the old tree are never disrupted.
func (*Router) Route ¶
Route creates a pattern-scoped route group. The callback receives a sub-Router that shares the parent's middleware registry and route tracking. When the router is unsealed, the route is deferred until Seal().
Calling Route with the same pattern more than once on the same parent is allowed: subsequent calls attach to the sub-mux created by the first call instead of panicking. Each call's body runs inside its own chi.Group, so middleware added via Use() inside a given block only applies to handlers registered in that block.
func (*Router) Routes ¶
func (r *Router) Routes() map[string][]RegisteredRoute
Routes returns a snapshot of all explicitly registered routes grouped by service. Implicit HEAD routes (auto-registered alongside GET) are excluded.
func (*Router) Seal ¶
func (r *Router) Seal()
Seal builds the routing tree from the deferred middleware and route registrations and publishes it. Middleware is applied first (via chi.Use), then route operations are replayed in order. After Seal, structural changes rebuild the tree and swap it in atomically. Seal is idempotent — calling it on an already-sealed router is a no-op.
func (*Router) ServeHTTP ¶
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)
ServeHTTP implements http.Handler, delegating to the published Chi tree. The tree is loaded atomically with no lock, so serving never contends with runtime route mutations.
func (*Router) SetErrorHandler ¶
func (r *Router) SetErrorHandler(h ErrorHandler)
SetErrorHandler configures the function that converts DI-aware handler errors into HTTP responses. Must be called before Handle() registrations.
func (*Router) Use ¶
func (r *Router) Use(middleware ...Middleware)
Use applies middleware to the router. Each Middleware is resolved (by name from the registry or used directly) and applied via chi's Use. Panics if a named middleware is not registered. When the router is unsealed, middleware is queued and applied during Seal().
func (*Router) UseMiddlewareByName ¶
UseMiddlewareByName applies a middleware to the router by resolving it from the registry using its name. Panics if the middleware is not registered.
type Rows ¶
Rows represents the result set of a query. Mirrors the standard database/sql Rows interface so implementations can wrap it directly.
type Scope ¶
type Scope struct {
// contains filtered or unexported fields
}
Scope is a resolution context for scoped service lifetimes.
func RequestScope ¶
RequestScope returns the per-request Scope from the request context. Panics if called outside the scope middleware (i.e. before InitServices installs it, or on a non-App-managed handler).
func (*Scope) Close ¶
Close calls Close(ctx) on all scoped Service instances resolved in this scope, in reverse resolution order, so a scoped service can still use the scoped dependencies it was built from.
func (*Scope) MustResolve ¶ added in v0.5.0
MustResolve is like Resolve but panics if the service cannot be resolved.
func (*Scope) Resolve ¶ added in v0.5.0
Resolve returns the instance registered for T within this scope. A scoped T is built once per Scope and cached for the life of the scope; singletons come from the owning container, and transients are built fresh. Scoped instances that implement Service are closed in reverse resolution order by Scope.Close.
type SecurityHeadersOption ¶
type SecurityHeadersOption func(*SecurityHeadersOptions)
SecurityHeadersOption is a functional option for configuring SecurityHeaders.
func WithSecurityHeadersContentSecurityPolicy ¶
func WithSecurityHeadersContentSecurityPolicy(val string) SecurityHeadersOption
WithSecurityHeadersContentSecurityPolicy sets the Content-Security-Policy header value. Pass an empty string to disable this header. No default is applied, since CSP values are highly application-specific.
func WithSecurityHeadersContentTypeOptions ¶
func WithSecurityHeadersContentTypeOptions(val string) SecurityHeadersOption
WithSecurityHeadersContentTypeOptions sets the X-Content-Type-Options header value. Pass an empty string to disable this header. Default: "nosniff".
func WithSecurityHeadersCrossOriginOpenerPolicy ¶
func WithSecurityHeadersCrossOriginOpenerPolicy(val string) SecurityHeadersOption
WithSecurityHeadersCrossOriginOpenerPolicy sets the Cross-Origin-Opener-Policy header value. Pass an empty string to disable this header. No default is applied.
func WithSecurityHeadersCrossOriginResourcePolicy ¶
func WithSecurityHeadersCrossOriginResourcePolicy(val string) SecurityHeadersOption
WithSecurityHeadersCrossOriginResourcePolicy sets the Cross-Origin-Resource-Policy header value. Pass an empty string to disable this header. No default is applied.
func WithSecurityHeadersFrameOptions ¶
func WithSecurityHeadersFrameOptions(val string) SecurityHeadersOption
WithSecurityHeadersFrameOptions sets the X-Frame-Options header value. Pass an empty string to disable this header. Default: "DENY".
func WithSecurityHeadersPermissionsPolicy ¶
func WithSecurityHeadersPermissionsPolicy(val string) SecurityHeadersOption
WithSecurityHeadersPermissionsPolicy sets the Permissions-Policy header value. Pass an empty string to disable this header. Default: "camera=(), microphone=(), geolocation=()".
func WithSecurityHeadersReferrerPolicy ¶
func WithSecurityHeadersReferrerPolicy(val string) SecurityHeadersOption
WithSecurityHeadersReferrerPolicy sets the Referrer-Policy header value. Pass an empty string to disable this header. Default: "strict-origin-when-cross-origin".
func WithSecurityHeadersStrictTransportSecurity ¶
func WithSecurityHeadersStrictTransportSecurity(val string) SecurityHeadersOption
WithSecurityHeadersStrictTransportSecurity sets the Strict-Transport-Security (HSTS) header value. Pass an empty string to disable this header. No default is applied, since HSTS should only be enabled once the site is fully served over HTTPS.
type SecurityHeadersOptions ¶
type SecurityHeadersOptions struct {
// contains filtered or unexported fields
}
SecurityHeadersOptions configures which security headers are set by the SecurityHeaders middleware.
type ServerSettings ¶
type ServerSettings struct {
// Host specifies the hostname or IP address where the server will be
// hosted: an IP address or an RFC 1123 hostname. ConfigProvider.Bind
// checks it against the validate tag, for shape only, with no DNS lookup.
Host string `validate:"required,max=253,ip|hostname_rfc1123"`
// Port is the port number on which the server listens for incoming requests.
Port int
// ReadTimeout is the maximum duration for reading the entire request.
ReadTimeout time.Duration
// WriteTimeout is the maximum duration before timing out writes of the response.
WriteTimeout time.Duration
// IdleTimeout is the maximum time to wait for the next request when keep-alives are enabled.
IdleTimeout time.Duration
// ShutdownTimeout is how long to wait for in-flight requests to complete
// during graceful shutdown.
ShutdownTimeout time.Duration
// MaxJSONBodyBytes caps how many bytes of the request body
// Context.BindJSON reads; a larger body fails with a 413 *Error. It
// defaults to 1 MiB. Zero or a negative value means no limit. It does not
// apply to handlers that read the body themselves.
MaxJSONBodyBytes int64
}
ServerSettings defines the configuration for a server, including host, port, timeouts, and graceful shutdown settings.
type Service ¶
type Service interface {
// Name returns the unique identifier for this service (e.g., "gas/auth").
Name() string
// Init initializes the service. Called automatically by the DI container
// after construction. Services register routes, middleware, migrations,
// and event subscriptions here.
//
// For a singleton, ctx is the caller's startup context (or RestartService
// context) with the service's init deadline added; for a scoped service it
// is the scope's context. Init must honor ctx: when the deadline passes
// the container cancels ctx and waits for Init to return, so an Init that
// ignores it holds up startup. ctx is canceled as soon as Init returns,
// so work that outlives Init, such as a background goroutine, must not
// derive from it.
Init(ctx context.Context) error
// Close gracefully shuts down the service. Called at App shutdown
// (singletons) or when a Scope is closed (scoped services). ctx is the
// caller's shutdown context and bounds the shutdown work.
Close(ctx context.Context) error
}
Service is the core interface for lifecycle-managed services. Any type registered with the DI container that implements this interface will have Init(ctx) called after construction and Close(ctx) called at shutdown (singletons) or scope end (scoped). This holds however the value was registered: a pre-built instance passed to WithServiceInstance is initialized too. Transient services cannot implement this interface — registration will be rejected.
Implementing it partially is rejected too. Init and Close are the managed lifecycle, so a type declaring either one must implement all three. A type that declares one and stops is not a Service and would be silently inert: Init never runs, Close never runs, and the kill switch cannot see it. BuildAll returns an error naming the missing or mis-typed methods instead.
Name is not a trigger. Declaring only Name leaves a type an ordinary dependency, and the same registration options carry those — loggers, config providers, per-request values — which must keep working.
A type from another package that declares Close (an io.Closer such as *sql.DB) cannot be given the remaining methods; wrap it in a service of your own rather than registering it directly.
type ServiceContainer ¶
type ServiceContainer struct {
// contains filtered or unexported fields
}
ServiceContainer is a dependency injection container that manages service registration, construction, and lifetime-scoped resolution.
func NewServiceContainer ¶
func NewServiceContainer(opts ...ServiceContainerOption) *ServiceContainer
NewServiceContainer creates a new ServiceContainer.
func (*ServiceContainer) BuildAll ¶
func (c *ServiceContainer) BuildAll(ctx context.Context) error
BuildAll initializes pre-registered Service instances, then eagerly resolves all singleton services in dependency order. Transient and scoped services are validated but not constructed.
Implementing Service means the container manages the lifecycle, so a pre-built instance is initialized like any other service. It is initialized before anything the container constructs, matching the order it became available, so reversing that order at shutdown stays correct.
func (*ServiceContainer) CanResolve ¶
func (c *ServiceContainer) CanResolve[T any]() bool
CanResolve reports whether the container has an instance or registration for exactly T, which is when Resolve can find it. An interface T is not resolvable just because a registered type implements it: register the constructor under the interface instead. It answers from the registry alone and builds nothing.
func (*ServiceContainer) EachInstance ¶
func (c *ServiceContainer) EachInstance(fn func(reflect.Value))
EachInstance iterates all built singleton instances (including pre-registered ones) in the order they became available. A dependency is always constructed and cached before the constructor that consumes it returns, so this order is a valid initialization order: reversing it is safe for shutdown.
func (*ServiceContainer) MustResolve ¶ added in v0.4.0
func (c *ServiceContainer) MustResolve[T any]() T
MustResolve is like Resolve but panics if the service cannot be resolved.
func (*ServiceContainer) NewScope ¶
func (c *ServiceContainer) NewScope(ctx context.Context) *Scope
NewScope creates a scoped resolution context. Scoped services resolved within this scope share instances; singletons delegate to the container. ctx is passed to the Init of every scoped service the scope builds; in an App it is the request's context.
func (*ServiceContainer) RegisterScopedService ¶ added in v0.4.0
func (c *ServiceContainer) RegisterScopedService[T any](ctor any)
RegisterScopedService registers a constructor for type T with the Scoped lifetime, so one T is built per Scope and shared within it. Scoped services cannot be resolved from the container itself; see NewScope. See RegisterService for the accepted signatures.
func (*ServiceContainer) RegisterService ¶ added in v0.4.0
func (c *ServiceContainer) RegisterService[T any](ctor any, lifetime ServiceLifetime)
RegisterService registers a constructor for type T with the given lifetime. The constructor takes its dependencies as parameters and returns T, or T and an error:
func(DepA, DepB, ...) T func(DepA, DepB, ...) (T, error)
Dependencies are resolved from the container by their parameter types, so every one of them must itself be registered by the time the service is built.
Panics if ctor is not a function the container can call: it must be non-variadic, return one or two values, produce a first result assignable to T (or implementing T when T is an interface), and, when it returns two, have error as the second. See validateCtorShape.
Panics if lifetime is Transient and T implements Service, because transient services cannot have managed lifecycles. Use Singleton or Scoped instead.
A type that declares Init or Close but does not fully implement Service is rejected by BuildAll; see validateServiceShape.
Registering a type twice replaces the earlier registration.
func (*ServiceContainer) RegisterServiceInstance ¶ added in v0.4.0
func (c *ServiceContainer) RegisterServiceInstance[T any](val T)
RegisterServiceInstance registers an already-built value under T, the static type at the call site, not the dynamic type of val. Passing an interface-typed variable therefore makes the value resolvable as that interface and not as its concrete type; pass the concrete type, or name T explicitly, to control which one it registers under.
The value is treated as a singleton. The container owns its lifecycle from here on, so do not call Init yourself before registering, or it runs twice.
func (*ServiceContainer) RegisterSingletonService ¶ added in v0.4.0
func (c *ServiceContainer) RegisterSingletonService[T any](ctor any)
RegisterSingletonService registers a constructor for type T with the Singleton lifetime, so one T is built and shared by every consumer. See RegisterService for the accepted signatures.
func (*ServiceContainer) RegisterTransientService ¶ added in v0.4.0
func (c *ServiceContainer) RegisterTransientService[T any](ctor any)
RegisterTransientService registers a constructor for type T with the Transient lifetime, so a fresh T is built on every resolution. T must not implement Service. See RegisterService for the accepted signatures.
func (*ServiceContainer) Resolve ¶ added in v0.4.0
func (c *ServiceContainer) Resolve[T any]() (T, error)
Resolve returns the instance registered for T, building it and its dependencies on first use and caching it when T is a singleton. Scoped registrations cannot be resolved here and return an error; call NewScope and resolve from the Scope instead.
type ServiceContainerOption ¶ added in v0.5.5
type ServiceContainerOption func(*ServiceContainerOptions)
ServiceContainerOption configures a ServiceContainer at construction.
func WithServiceContainerServiceTimeout ¶ added in v0.5.5
func WithServiceContainerServiceTimeout(timeout time.Duration) ServiceContainerOption
WithServiceContainerServiceTimeout sets the deadline the container gives each singleton service's Init. The default is 15 seconds, and 0 disables the deadline so only the caller's context applies. A service that implements ServiceInitTimeout overrides it for itself.
Panics if timeout is negative.
type ServiceContainerOptions ¶ added in v0.5.5
type ServiceContainerOptions struct {
// contains filtered or unexported fields
}
ServiceContainerOptions holds the settings a ServiceContainer is built with. Its fields are set through ServiceContainerOption values passed to NewServiceContainer.
type ServiceInitTimeout ¶ added in v0.5.5
type ServiceInitTimeout interface {
// InitTimeout returns how long the container waits for Init.
InitTimeout() time.Duration
}
ServiceInitTimeout is implemented by a singleton Service that needs a different Init deadline than the container default (see WithServiceInitTimeout). It applies at startup and on RestartService. When the deadline passes, Init's context is canceled and, once Init returns, the call fails with an error that wraps context.DeadlineExceeded. If Init ignored the cancellation and returned nil, the service is closed again before the error is returned. 0 means no deadline; a negative value fails Init without running it.
type ServiceLifetime ¶
type ServiceLifetime uint8
ServiceLifetime controls how a service instance is created and cached.
const ( // ServiceLifetimeSingleton services are created once and shared across all consumers. ServiceLifetimeSingleton ServiceLifetime = iota // ServiceLifetimeScoped services are created once per Scope. ServiceLifetimeScoped // ServiceLifetimeTransient services are created fresh on every resolution. ServiceLifetimeTransient )
func (ServiceLifetime) String ¶
func (l ServiceLifetime) String() string
String returns the lowercase name of the lifetime ("singleton", "scoped", "transient"), or "unknown" for a value outside that set.
type SystemAllServicesInitialized ¶
type SystemAllServicesInitialized struct {
Event[SystemAllServicesInitializedPayload]
}
SystemAllServicesInitialized is emitted when all services have been successfully initialized.
type SystemAllServicesInitializedPayload ¶
type SystemAllServicesInitializedPayload struct{}
SystemAllServicesInitializedPayload is an empty payload for the all-services-initialized event.
type SystemServerShuttingDown ¶
type SystemServerShuttingDown struct {
Event[SystemServerShuttingDownPayload]
}
SystemServerShuttingDown is emitted when the HTTP server is shutting down. For code that should run on any shutdown (not just HTTP), subscribe to SystemShuttingDown instead.
type SystemServerShuttingDownPayload ¶
type SystemServerShuttingDownPayload struct{}
SystemServerShuttingDownPayload is an empty payload for the server-shutting-down event.
type SystemServiceClosed ¶
type SystemServiceClosed struct {
Event[SystemServiceClosedPayload]
}
SystemServiceClosed is emitted when a service is closed at runtime.
type SystemServiceClosedPayload ¶
type SystemServiceClosedPayload struct {
ServiceName string
}
SystemServiceClosedPayload carries the name of the closed service.
type SystemServiceInitialized ¶
type SystemServiceInitialized struct {
Event[SystemServiceInitializedPayload]
}
SystemServiceInitialized is emitted when a service is (re-)initialized at runtime.
type SystemServiceInitializedPayload ¶
type SystemServiceInitializedPayload struct {
ServiceName string
}
SystemServiceInitializedPayload carries the name of the initialized service.
type SystemShuttingDown ¶
type SystemShuttingDown struct {
Event[SystemShuttingDownPayload]
}
SystemShuttingDown is emitted when a Worker or App begins its shutdown sequence. It fires for both HTTP (App) and non-HTTP (Worker) workloads.
type SystemShuttingDownPayload ¶
type SystemShuttingDownPayload struct{}
SystemShuttingDownPayload is an empty payload for the shutting-down event.
type TemplateProvider ¶
type TemplateProvider interface {
// Get returns the raw template content by name.
Get(ctx context.Context, name string) ([]byte, error)
// List returns all available template names.
List(ctx context.Context) ([]string, error)
// Register adds or replaces a template by name and raw content.
Register(ctx context.Context, name string, content []byte) error
// RegisterFS walks an fs.FS and registers every template file found.
RegisterFS(ctx context.Context, fsys fs.FS) error
}
TemplateProvider abstracts template storage and retrieval. Implementations may be backed by a filesystem, database, memory, or any combination. Consumers such as UIProvider (HTML rendering) and EmailProvider (email templating) resolve raw template content through this interface.
type TemplatedEmail ¶
type TemplatedEmail struct {
SubjectTemplate string
TextTemplate string
HTMLTemplate string
Data any
Email
}
TemplatedEmail represents an email message where templates and data define subject, text, and HTML bodies.
type UIProvider ¶
type UIProvider interface {
Render(w http.ResponseWriter, name string, data any) error
RenderFragment(
w http.ResponseWriter,
name string,
data any,
) error // renders without layout wrapper, useful for HTMX
RenderWithStatus(w http.ResponseWriter, status int, name string, data any) error
RegisterFuncs(funcs template.FuncMap)
}
UIProvider abstracts template rendering. Implemented by gas/ui or any other UI service. Template storage and retrieval is handled by TemplateProvider; UIProvider focuses on compilation and rendering.
type WithGasEnv ¶ added in v0.5.5
type WithGasEnv struct {
GasEnv Environment
}
WithGasEnv is embedded in a configuration struct to carry the application environment. Its GasEnv field binds from the configuration's GasEnv key, which the gasenv extension sets during load; without the extension, nothing sets it unless a provider does, and GasEnv stays empty.
type Worker ¶
type Worker struct {
// contains filtered or unexported fields
}
Worker manages service lifecycle, dependency injection, and the event bus without an HTTP server. Use it for non-HTTP environments such as AWS Lambda, background job processors, or CLI tools. For HTTP servers, use App which embeds Worker and adds routing, CSRF protection, and an HTTP listener.
func NewWorker ¶
NewWorker creates a Worker with the given options. Only WorkerOption values are applied; passing an AppOption panics.
func (*Worker) ActiveServices ¶
ActiveServices returns the names of all currently active services.
func (*Worker) ActiveServicesMap ¶
ActiveServicesMap returns a map of all currently active services. This is a test helper.
func (*Worker) CheckHealth ¶
CheckHealth runs CheckHealth concurrently on every active service that implements HealthReporter and returns a map of service name to result (nil if healthy). Services that do not implement HealthReporter are omitted.
func (*Worker) CheckReady ¶
CheckReady runs CheckReady concurrently on every active service that implements ReadyReporter and returns a map of service name to result (nil if ready). Services that do not implement ReadyReporter are omitted.
func (*Worker) CloseService ¶
CloseService performs the kill-switch sequence for the service registered under T at runtime:
w.CloseService[*auth.Service](ctx)
Infrastructure is cleaned up first, so even if Close panics or fails, the service's routes and event subscriptions are already gone. Its routes then answer 503, and named middleware it owns is disabled wherever it is used.
The service is looked up among the instances the container has already built, so a registration that was never initialized is reported as an error rather than constructed here. Returns an error if T has no built instance or is not currently active, or Shutdown has begun; a failure from the service's own Close is logged, not returned, because the teardown above it has already happened.
ctx is passed to the service's Close.
func (*Worker) ConfigProvider ¶
func (w *Worker) ConfigProvider() ConfigProvider
ConfigProvider resolves the ConfigProvider from the DI container. Returns nil if no ConfigProvider is registered.
func (*Worker) InitServices ¶
InitServices builds all singletons via the DI container (which calls Init(ctx) on each Service automatically), then collects Service instances for runtime management. Collection follows the container's instance order, so serviceOrder is a genuine initialization order and Shutdown can reverse it safely. ctx is passed to the Init of every singleton, each under its init deadline (see WithServiceInitTimeout).
InitServices runs once. If it fails, it closes, in reverse order, every service it had initialized, with ctx stripped of its cancellation, and returns the error joined with any Close errors; later calls return that same error.
func (*Worker) MigrationManager ¶
func (w *Worker) MigrationManager() MigrationManager
MigrationManager resolves the MigrationManager from the DI container. Returns nil if no MigrationManager is registered.
func (*Worker) ReadyFunc ¶ added in v0.5.0
func (w *Worker) ReadyFunc(fn func(*ServiceContainer) error)
ReadyFunc registers a function that runs after all services are initialized and migrations are applied, but before Run blocks or Start returns. It is the imperative form of WithReadyFunc and may be called any number of times before startup; the funcs run in registration order and the first error aborts startup.
Safe for concurrent use. Panics if called once Start has reached the ready hooks, because the func would never run.
func (*Worker) RegisterScopedService ¶ added in v0.4.0
RegisterScopedService registers a constructor for type T with the Scoped lifetime on the worker's service container, so one T is built per Scope. In an App, each request gets its own scope; see ResolveFromRequestScope.
func (*Worker) RegisterService ¶ added in v0.4.0
func (w *Worker) RegisterService[T any](ctor any, lifetime ServiceLifetime)
RegisterService registers a constructor for type T with the given lifetime on the worker's service container. It is the imperative form of WithService, for wiring done after the Worker is built. See ServiceContainer.RegisterService for the accepted constructor signatures and the panics on a bad one.
func (*Worker) RegisterServiceInstance ¶ added in v0.4.0
RegisterServiceInstance registers an already-built value on the worker's service container under T, the static type at the call site, not the dynamic type of val. Treated as a singleton; see ServiceContainer.RegisterServiceInstance for the lifecycle it takes on.
func (*Worker) RegisterSingletonService ¶ added in v0.4.0
RegisterSingletonService registers a constructor for type T with the Singleton lifetime on the worker's service container, so one T is built and shared by every consumer.
func (*Worker) RegisterTransientService ¶ added in v0.4.0
RegisterTransientService registers a constructor for type T with the Transient lifetime on the worker's service container, so a fresh T is built on every resolution. T must not implement Service.
func (*Worker) RestartService ¶
RestartService re-initializes the previously closed service registered under T, re-running Init on the same instance so it registers its routes, middleware and subscriptions again:
w.RestartService[*auth.Service](ctx)
The service must have been registered with the Worker at construction time and built during InitServices (that is, it must be a singleton in serviceOrder). Returns an error if T has no built instance, is already active, Shutdown has begun, or its Init fails. ctx is passed to the service's Init.
func (*Worker) Run ¶
Run initializes services, runs migrations and ready hooks, then blocks until a SIGINT or SIGTERM signal is received before shutting down. For non-blocking lifecycle control, use Start and Shutdown directly. Startup and shutdown use context.Background().
func (*Worker) ServiceContainer ¶
func (w *Worker) ServiceContainer() *ServiceContainer
ServiceContainer returns the Worker's dependency injection container.
func (*Worker) Shutdown ¶
Shutdown emits SystemShuttingDown and closes every active service in reverse initialization order, passing ctx to each Close. A service already closed by CloseService is skipped, and each service Shutdown closes leaves ActiveServices. A failing Close does not stop the rest; every Close error is returned, joined, and not logged.
Shutdown runs once. Later calls, including concurrent ones, wait for the first to finish and return nil. Once Shutdown has begun, CloseService and RestartService return an error.
func (*Worker) Start ¶
Start initializes all services, runs pending migrations, and executes ready hooks. It does NOT block — use it when you need explicit lifecycle control (e.g. AWS Lambda). Call Shutdown when done. ctx is passed to the Init of every service built during startup; canceling it aborts startup.
A Start that fails leaves nothing running: it closes, in reverse order, every service it had initialized, with ctx stripped of its cancellation, and returns the startup error joined with any Close errors. The Worker cannot be started again: every later Start returns the same error, and a later Shutdown has nothing to close.
type WorkerOption ¶
type WorkerOption func(*Worker)
WorkerOption configures a Worker. It is the base option type for DI registration, ready hooks, and other non-HTTP concerns. Both NewWorker and NewApp accept WorkerOption values.
func WithReadyFunc ¶
func WithReadyFunc(fn func(*ServiceContainer) error) WorkerOption
WithReadyFunc registers a function that runs after all services are initialized and migrations are applied but before Run blocks or Start returns. Multiple funcs are called in registration order; any error aborts startup.
func WithScopedService ¶
func WithScopedService[T any](ctor any) WorkerOption
WithScopedService registers a service constructor with a scoped lifetime.
func WithService ¶
func WithService[T any](ctor any, lifetime ServiceLifetime) WorkerOption
WithService registers a constructor-based service with the given lifetime.
func WithServiceInitTimeout ¶ added in v0.5.5
func WithServiceInitTimeout(timeout time.Duration) WorkerOption
WithServiceInitTimeout sets the deadline the Worker's service container gives each singleton service's Init, at startup and on RestartService. When it passes, Init's context is canceled and startup fails once Init returns. The default is 15 seconds, and 0 disables the deadline so only the caller's context applies. A service that implements ServiceInitTimeout overrides it for itself.
Panics if timeout is negative.
func WithServiceInstance ¶
func WithServiceInstance[T any](val T) WorkerOption
WithServiceInstance registers a pre-built service instance (singleton).
Pre-built means pre-constructed, not pre-initialized: if the value implements Service, the container calls Init(ctx) on it during InitServices like any other service, and Close(ctx) at shutdown. Implementing Service is what hands the lifecycle to the container, however the value got there. Do not call Init yourself before registering, or it runs twice.
func WithSingletonService ¶
func WithSingletonService[T any](ctor any) WorkerOption
WithSingletonService registers a singleton service constructor.
func WithTransientService ¶
func WithTransientService[T any](ctor any) WorkerOption
WithTransientService registers a transient service constructor.
Source Files
¶
- app.go
- app_test_helpers.go
- config.go
- context.go
- di_errors.go
- di_guard.go
- doc.go
- environment.go
- errors.go
- eventbus.go
- events.go
- handler.go
- log_route_map.go
- logger.go
- middleware.go
- migration.go
- nop_logger.go
- option.go
- principal.go
- providers.go
- request_scope.go
- router.go
- service.go
- service_management.go
- service_registry.go
- slog_logger.go
- validation.go
- worker.go