This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

App

API reference for the Rivaas App package - a batteries-included web framework with integrated observability.

This is the API reference for the rivaas.dev/app package. For learning-focused documentation, see the App Guide.

Overview

The App package provides a high-level, opinionated framework built on top of the Rivaas router. It includes:

  • Integrated observability (metrics, tracing, logging)
  • Lifecycle management with hooks
  • Graceful shutdown handling
  • Health and debug endpoints
  • OpenAPI spec generation
  • Request binding and validation

Package Information

  • Import Path: rivaas.dev/app
  • Go Version: 1.25+
  • License: Apache 2.0

Architecture

┌─────────────────────────────────────────┐
│           Application Layer             │
│  (app package)                          │
│                                         │
│  • Configuration Management             │
│  • Lifecycle Hooks                      │
│  • Observability Integration            │
│  • Server Management                    │
│  • Request Binding/Validation           │
└──────────────┬──────────────────────────┘
               │
               ▼
┌─────────────────────────────────────────┐
│           Router Layer                  │
│  (router package)                       │
│                                         │
│  • HTTP Routing                         │
│  • Middleware Chain                     │
│  • Request Context                      │
│  • Path Parameters                      │
└──────────────┬──────────────────────────┘
               │
               ▼
┌─────────────────────────────────────────┐
│        Standard Library                 │
│  (net/http)                             │
└─────────────────────────────────────────┘

Quick Reference

Core Types

  • App - Main application type
  • Context - Request context with app-level features
  • HandlerFunc - Handler function type

Key Functions

  • New() - Create a new app (returns error)
  • MustNew() - Create a new app (panics on error)

Configuration

API Reference

Resources

App Type

The main application type that wraps the router with app-level features.

type App struct {
    // contains filtered or unexported fields
}

Creating Apps

// Returns (*App, error) for error handling
a, err := app.New(
    app.WithServiceName("my-api"),
    app.WithServiceVersion("v1.0.0"),
)
if err != nil {
    log.Fatal(err)
}

// Panics on error (like regexp.MustCompile)
a := app.MustNew(
    app.WithServiceName("my-api"),
)

HTTP Methods

Register routes for HTTP methods:

a.GET(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
a.POST(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
a.PUT(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
a.DELETE(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
a.PATCH(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
a.HEAD(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
a.OPTIONS(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
a.Any(path string, handler HandlerFunc, opts ...RouteOption) *route.Route

Server Management

a.Start(ctx context.Context, addr string) error
a.StartTLS(ctx context.Context, addr, certFile, keyFile string) error
a.StartMTLS(ctx context.Context, addr string, cert tls.Certificate, opts ...MTLSOption) error

Lifecycle Hooks

a.OnStart(fn func(context.Context) error)
a.OnReady(fn func())
a.OnShutdown(fn func(context.Context))
a.OnStop(fn func())
a.OnRoute(fn func(*route.Route))

See Lifecycle Hooks for details.

Context Type

Request context that extends router.Context with app-level features.

type Context struct {
    *router.Context
    // contains filtered or unexported fields
}

Request Binding

c.Bind(out any) error
c.BindJSONStrict(out any) error
c.BindAndValidate(out any, opts ...validation.Option) error
c.BindAndValidateStrict(out any, opts ...validation.Option) error
c.MustBindAndValidate(out any, opts ...validation.Option) bool

Error Handling

c.Error(err error)
c.ErrorStatus(err error, status int)
c.NotFound(message string)
c.BadRequest(message string)
c.Unauthorized(message string)
c.Forbidden(message string)
c.InternalError(err error)

Logging

c.Logger() *slog.Logger

See Context API for complete reference.

HandlerFunc

Handler function type that receives an app Context.

type HandlerFunc func(*Context)

Example:

func handler(c *app.Context) {
    c.JSON(http.StatusOK, data)
}

a.GET("/", handler)

Next Steps

1 - API Reference

Complete API reference for the App package.

Core Functions

New

func New(opts ...Option) (*App, error)

Creates a new App instance with the given options. Returns an error if configuration is invalid.

Parameters:

  • opts - Configuration options

Returns:

  • *App - The app instance
  • error - Configuration validation errors

Example:

a, err := app.New(
    app.WithServiceName("my-api"),
    app.WithServiceVersion("v1.0.0"),
)
if err != nil {
    log.Fatal(err)
}

MustNew

func MustNew(opts ...Option) *App

Creates a new App instance or panics on error. Use for initialization in main() functions.

Parameters:

  • opts - Configuration options

Returns:

  • *App - The app instance

Panics: If configuration is invalid

Example:

a := app.MustNew(
    app.WithServiceName("my-api"),
)

App Methods

HTTP Method Shortcuts

func (a *App) GET(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
func (a *App) POST(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
func (a *App) PUT(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
func (a *App) DELETE(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
func (a *App) PATCH(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
func (a *App) HEAD(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
func (a *App) OPTIONS(path string, handler HandlerFunc, opts ...RouteOption) *route.Route
func (a *App) Any(path string, handler HandlerFunc, opts ...RouteOption) *route.Route

Register routes for HTTP methods.

Middleware

func (a *App) Use(middleware ...HandlerFunc)

Adds middleware to the app. Middleware executes for all routes registered after Use().

Route Groups

func (a *App) Group(prefix string, middleware ...HandlerFunc) *Group
func (a *App) Version(version string) *VersionGroup

Create route groups and version groups.

Static Files

func (a *App) Static(prefix, root string)
func (a *App) File(path, filepath string)
func (a *App) StaticFS(prefix string, fs http.FileSystem)
func (a *App) NoRoute(handler HandlerFunc)

Serve static files and set custom 404 handler.

Server Management

func (a *App) Start(ctx context.Context, addr string) error
func (a *App) StartTLS(ctx context.Context, addr, certFile, keyFile string) error
func (a *App) StartMTLS(ctx context.Context, addr string, cert tls.Certificate, opts ...MTLSOption) error

Start HTTP, HTTPS, or mTLS servers with graceful shutdown.

Lifecycle Hooks

func (a *App) OnStart(fn func(context.Context) error)
func (a *App) OnReady(fn func())
func (a *App) OnShutdown(fn func(context.Context))
func (a *App) OnStop(fn func())
func (a *App) OnRoute(fn func(*route.Route))

Register lifecycle hooks. See Lifecycle Hooks for details.

Accessors

func (a *App) Router() *router.Router
func (a *App) Metrics() *metrics.Recorder
func (a *App) Tracing() *tracing.Tracer
func (a *App) Readiness() *ReadinessManager
func (a *App) ServiceName() string
func (a *App) ServiceVersion() string
func (a *App) Environment() string

Access underlying components and configuration.

Route Management

func (a *App) Route(name string) (*route.Route, bool)
func (a *App) Routes() []*route.Route
func (a *App) URLFor(routeName string, params map[string]string, query map[string][]string) (string, error)
func (a *App) MustURLFor(routeName string, params map[string]string, query map[string][]string) string

Route lookup and URL generation. Router must be frozen (after Start()).

Metrics

func (a *App) GetMetricsHandler() (http.Handler, error)
func (a *App) GetMetricsServerAddress() string

Access metrics handler and server address.

Logging

func (a *App) BaseLogger() *slog.Logger

Returns the application’s base logger. Never returns nil.

Testing

func (a *App) Test(req *http.Request, opts ...TestOption) (*http.Response, error)
func (a *App) TestJSON(method, path string, body any, opts ...TestOption) (*http.Response, error)

Test routes without starting a server.

Helper Functions

ExpectJSON

func ExpectJSON(t testingT, resp *http.Response, statusCode int, out any)

Test helper that asserts response status and decodes JSON.

Generic Binding

func BindAndValidateInto[T any](c *Context, opts ...validation.Option) (T, error)
func MustBindAndValidateInto[T any](c *Context, opts ...validation.Option) (T, bool)

Type-safe binding with generics.

Types

HandlerFunc

type HandlerFunc func(*Context)

Handler function that receives an app Context.

TestOption

type TestOption func(*testConfig)

func WithTimeout(d time.Duration) TestOption
func WithContext(ctx context.Context) TestOption

Options for testing.

Next Steps

2 - Configuration Options

App-level configuration options reference.

Service Configuration

WithServiceName

func WithServiceName(name string) Option

Sets the service name used in observability metadata. This includes metrics, traces, and logs. If empty, validation fails.

Default: "rivaas-app"

WithServiceVersion

func WithServiceVersion(version string) Option

Sets the service version used in observability and API documentation. Must be non-empty or validation fails.

Default: "1.0.0"

WithEnvironment

func WithEnvironment(env string) Option

Sets the environment mode. Valid values: "development", "production". Invalid values cause validation to fail.

Default: "development"

Server Configuration

WithServer

func WithServer(opts ...ServerOption) Option

Configures server settings. See Server Options for sub-options.

Observability

WithObservability

func WithObservability(opts ...ObservabilityOption) Option

Configures all observability components (metrics, tracing, logging). See Observability Options for sub-options.

Endpoints

WithHealthEndpoints

func WithHealthEndpoints(opts ...HealthOption) Option

Enables health endpoints. See Health Options for sub-options.

WithDebugEndpoints

func WithDebugEndpoints(opts ...DebugOption) Option

Enables debug endpoints. See Debug Options for sub-options.

Middleware

WithMiddleware

func WithMiddleware(middlewares ...HandlerFunc) Option

Adds middleware during app initialization. Multiple calls accumulate.

WithoutDefaultMiddleware

func WithoutDefaultMiddleware() Option

Disables default middleware (recovery). Use when you want full control over middleware.

Router

WithRouter

func WithRouter(opts ...router.Option) Option

Passes router options to the underlying router. Multiple calls accumulate.

OpenAPI

WithOpenAPI

func WithOpenAPI(opts ...openapi.Option) Option

Enables OpenAPI specification generation. Service name and version are automatically injected from app-level configuration.

Error Formatting

WithErrorFormatter

func WithErrorFormatter(formatter errors.Formatter) Option

Configures a single error formatter for all error responses.

WithErrorFormatters

func WithErrorFormatters(formatters map[string]errors.Formatter) Option

Configures multiple error formatters with content negotiation based on Accept header.

WithDefaultErrorFormat

func WithDefaultErrorFormat(mediaType string) Option

Sets the default format when no Accept header matches. Only used with WithErrorFormatters.

Complete Example

a, err := app.New(
    // Service
    app.WithServiceName("orders-api"),
    app.WithServiceVersion("v2.0.0"),
    app.WithEnvironment("production"),
    
    // Server
    app.WithServer(
        app.WithReadTimeout(10 * time.Second),
        app.WithWriteTimeout(15 * time.Second),
        app.WithShutdownTimeout(30 * time.Second),
    ),
    
    // Observability
    app.WithObservability(
        app.WithLogging(logging.WithJSONHandler()),
        app.WithMetrics(),
        app.WithTracing(tracing.WithOTLP("localhost:4317")),
    ),
    
    // Health endpoints
    app.WithHealthEndpoints(
        app.WithReadinessCheck("database", dbCheck),
    ),
    
    // OpenAPI
    app.WithOpenAPI(
        openapi.WithSwaggerUI(true, "/docs"),
    ),
)

Next Steps

3 - Server Options

Server configuration options reference.

Server Options

These options are used with WithServer():

app.WithServer(
    app.WithReadTimeout(10 * time.Second),
    app.WithWriteTimeout(15 * time.Second),
)

Timeout Options

WithReadTimeout

func WithReadTimeout(d time.Duration) ServerOption

Maximum time to read entire request (including body). Must be positive.

Default: 10s

WithWriteTimeout

func WithWriteTimeout(d time.Duration) ServerOption

Maximum time to write response. Must be positive. Should be >= ReadTimeout.

Default: 10s

WithIdleTimeout

func WithIdleTimeout(d time.Duration) ServerOption

Maximum time to wait for next request on keep-alive connection. Must be positive.

Default: 60s

WithReadHeaderTimeout

func WithReadHeaderTimeout(d time.Duration) ServerOption

Maximum time to read request headers. Must be positive.

Default: 2s

WithShutdownTimeout

func WithShutdownTimeout(d time.Duration) ServerOption

Graceful shutdown timeout. Must be at least 1 second.

Default: 30s

Size Options

WithMaxHeaderBytes

func WithMaxHeaderBytes(n int) ServerOption

Maximum request header size in bytes. Must be at least 1KB (1024 bytes).

Default: 1MB (1048576 bytes)

Validation

Configuration is automatically validated:

  • All timeouts must be positive
  • ReadTimeout should not exceed WriteTimeout
  • ShutdownTimeout must be at least 1 second
  • MaxHeaderBytes must be at least 1KB

Invalid configuration causes app.New() to return an error.

4 - Observability Options

Observability configuration options reference (metrics, tracing, logging).

Observability Options

These options are used with WithObservability():

app.WithObservability(
    app.WithLogging(logging.WithJSONHandler()),
    app.WithMetrics(),
    app.WithTracing(tracing.WithOTLP("localhost:4317")),
)

Component Options

WithLogging

func WithLogging(opts ...logging.Option) ObservabilityOption

Enables structured logging with slog. Service name/version automatically injected.

WithMetrics

func WithMetrics(opts ...metrics.Option) ObservabilityOption

Enables metrics collection (Prometheus by default). Service name/version automatically injected.

WithTracing

func WithTracing(opts ...tracing.Option) ObservabilityOption

Enables distributed tracing. Service name/version automatically injected.

Metrics Server Options

WithMetricsOnMainRouter

func WithMetricsOnMainRouter(path string) ObservabilityOption

Mounts metrics endpoint on the main HTTP server (default: separate server).

WithMetricsSeparateServer

func WithMetricsSeparateServer(addr, path string) ObservabilityOption

Configures separate metrics server address and path.

Default: :9090/metrics

Path Filtering

WithExcludePaths

func WithExcludePaths(paths ...string) ObservabilityOption

Excludes exact paths from observability.

WithExcludePrefixes

func WithExcludePrefixes(prefixes ...string) ObservabilityOption

Excludes path prefixes from observability.

WithExcludePatterns

func WithExcludePatterns(patterns ...string) ObservabilityOption

Excludes paths matching regex patterns from observability.

WithoutDefaultExclusions

func WithoutDefaultExclusions() ObservabilityOption

Disables default path exclusions (/health*, /metrics, /debug/*).

Access Logging

WithAccessLogging

func WithAccessLogging(enabled bool) ObservabilityOption

Enables or disables access logging.

Default: true

WithLogOnlyErrors

func WithLogOnlyErrors() ObservabilityOption

Logs only errors and slow requests (reduces log volume).

Default: false in development, true in production

In production, this is automatically enabled to reduce log volume. Normal successful requests are not logged, but errors (status >= 400) and slow requests are always logged.

WithSlowThreshold

func WithSlowThreshold(d time.Duration) ObservabilityOption

Marks requests as slow if they exceed this duration.

Default: 1s

Example

app.WithObservability(
    // Components
    app.WithLogging(logging.WithJSONHandler()),
    app.WithMetrics(metrics.WithPrometheus(":9090", "/metrics")),
    app.WithTracing(tracing.WithOTLP("localhost:4317")),
    
    // Path filtering
    app.WithExcludePaths("/healthz", "/readyz"),
    app.WithExcludePrefixes("/internal/"),
    
    // Access logging
    app.WithLogOnlyErrors(),
    app.WithSlowThreshold(500 * time.Millisecond),
)

5 - Health Options

Health endpoint configuration options reference.

Health Options

These options are used with WithHealthEndpoints():

app.WithHealthEndpoints(
    app.WithReadinessCheck("database", dbCheck),
    app.WithHealthTimeout(800 * time.Millisecond),
)

Path Configuration

WithHealthPrefix

func WithHealthPrefix(prefix string) HealthOption

Mounts health endpoints under a prefix.

Default: "" (root)

WithHealthzPath

func WithHealthzPath(path string) HealthOption

Custom liveness probe path.

Default: "/healthz"

WithReadyzPath

func WithReadyzPath(path string) HealthOption

Custom readiness probe path.

Default: "/readyz"

Check Configuration

WithHealthTimeout

func WithHealthTimeout(d time.Duration) HealthOption

Timeout for each health check.

Default: 1s

WithLivenessCheck

func WithLivenessCheck(name string, fn CheckFunc) HealthOption

Adds a liveness check. Liveness checks should be dependency-free and fast.

WithReadinessCheck

func WithReadinessCheck(name string, fn CheckFunc) HealthOption

Adds a readiness check. Readiness checks verify external dependencies.

CheckFunc

type CheckFunc func(context.Context) error

Health check function that returns nil if healthy, error if unhealthy.

Example

app.WithHealthEndpoints(
    app.WithHealthPrefix("/_system"),
    app.WithHealthTimeout(800 * time.Millisecond),
    app.WithLivenessCheck("process", func(ctx context.Context) error {
        return nil
    }),
    app.WithReadinessCheck("database", func(ctx context.Context) error {
        return db.PingContext(ctx)
    }),
)

// Endpoints:
// GET /_system/healthz - Liveness (200 if all checks pass)
// GET /_system/readyz - Readiness (204 if all checks pass)

6 - Debug Options

Debug endpoint configuration options reference.

Debug Options

These options are used with WithDebugEndpoints():

app.WithDebugEndpoints(
    app.WithPprofIf(os.Getenv("PPROF_ENABLED") == "true"),
)

Path Configuration

WithDebugPrefix

func WithDebugPrefix(prefix string) DebugOption

Mounts debug endpoints under a custom prefix.

Default: "/debug"

pprof Options

WithPprof

func WithPprof() DebugOption

Enables pprof endpoints unconditionally.

WithPprofIf

func WithPprofIf(condition bool) DebugOption

Conditionally enables pprof endpoints based on a boolean condition.

Available Endpoints

When pprof is enabled:

  • GET /debug/pprof/ - Main pprof index
  • GET /debug/pprof/cmdline - Command line
  • GET /debug/pprof/profile - CPU profile
  • GET /debug/pprof/symbol - Symbol lookup
  • GET /debug/pprof/trace - Execution trace
  • GET /debug/pprof/allocs - Memory allocations
  • GET /debug/pprof/block - Block profile
  • GET /debug/pprof/goroutine - Goroutine profile
  • GET /debug/pprof/heap - Heap profile
  • GET /debug/pprof/mutex - Mutex profile
  • GET /debug/pprof/threadcreate - Thread creation profile

Security Warning

⚠️ Never enable pprof in production without proper authentication. Debug endpoints expose sensitive runtime information.

Example

// Development: enable unconditionally
app.WithDebugEndpoints(
    app.WithPprof(),
)

// Production: enable conditionally
app.WithDebugEndpoints(
    app.WithDebugPrefix("/_internal/debug"),
    app.WithPprofIf(os.Getenv("PPROF_ENABLED") == "true"),
)

7 - Context API

Context methods for request handling.

Request Binding

Bind

func (c *Context) Bind(out any) error

Automatically binds from all relevant sources based on struct tags (path, query, header, cookie, json, form).

BindJSONStrict

func (c *Context) BindJSONStrict(out any) error

Binds JSON with unknown field rejection.

BindAndValidate

func (c *Context) BindAndValidate(out any, opts ...validation.Option) error

Binds and validates in one call.

BindAndValidateStrict

func (c *Context) BindAndValidateStrict(out any, opts ...validation.Option) error

Binds JSON strictly (rejects unknown fields) and validates.

MustBindAndValidate

func (c *Context) MustBindAndValidate(out any, opts ...validation.Option) bool

Binds and validates, automatically sending error responses on failure. Returns true if successful.

Error Handling

Error

func (c *Context) Error(err error)

Sends a formatted error response using the configured formatter.

ErrorStatus

func (c *Context) ErrorStatus(err error, status int)

Sends an error response with explicit status code.

NotFound

func (c *Context) NotFound(message string)

Sends a 404 Not Found error.

BadRequest

func (c *Context) BadRequest(message string)

Sends a 400 Bad Request error.

Unauthorized

func (c *Context) Unauthorized(message string)

Sends a 401 Unauthorized error.

Forbidden

func (c *Context) Forbidden(message string)

Sends a 403 Forbidden error.

InternalError

func (c *Context) InternalError(err error)

Sends a 500 Internal Server Error.

Logging

Logger

func (c *Context) Logger() *slog.Logger

Returns the request-scoped logger with automatic context (HTTP metadata, trace IDs, request ID). Never returns nil.

Presence

Presence

func (c *Context) Presence() validation.PresenceMap

Returns the presence map for the current request (tracks which fields were present in JSON).

ResetBinding

func (c *Context) ResetBinding()

Resets binding metadata (useful for testing).

Router Context

The app Context embeds router.Context, providing access to all router features:

  • c.Request - HTTP request
  • c.Response - HTTP response writer
  • c.Param(name) - Path parameter
  • c.Query(name) - Query parameter
  • c.JSON(status, data) - Send JSON response
  • c.String(status, text) - Send text response
  • c.HTML(status, html) - Send HTML response
  • And more…

See Router Context API for complete router context reference.

8 - Lifecycle Hooks

Lifecycle hook APIs and execution order.

Hook Methods

OnStart

func (a *App) OnStart(fn func(context.Context) error)

Called before server starts. Hooks run sequentially and stop on first error.

Use for: Database connections, migrations, initialization that must succeed.

OnReady

func (a *App) OnReady(fn func())

Called after server starts listening. Hooks run asynchronously and don’t block startup.

Use for: Warmup tasks, service discovery registration.

OnShutdown

func (a *App) OnShutdown(fn func(context.Context))

Called during graceful shutdown. Hooks run in LIFO order with shutdown timeout.

Use for: Closing connections, flushing buffers, cleanup that must complete within timeout.

OnStop

func (a *App) OnStop(fn func())

Called after shutdown completes. Hooks run in best-effort mode and panics are caught.

Use for: Final cleanup that doesn’t need timeout.

OnRoute

func (a *App) OnRoute(fn func(*route.Route))

Called when a route is registered. Disabled after router freeze.

Use for: Route validation, logging, documentation generation.

Execution Flow

1. app.Start(ctx, ":8080") called
2. OnStart hooks execute (sequential, stop on error)
3. Server starts listening
4. OnReady hooks execute (async, non-blocking)
5. Server handles requests...
6. Context canceled (SIGTERM/SIGINT)
7. OnShutdown hooks execute (LIFO order, with timeout)
8. Server shutdown complete
9. OnStop hooks execute (best-effort, no timeout)
10. Process exits

Hook Characteristics

HookOrderError HandlingTimeoutAsync
OnStartSequentialStop on first errorNoNo
OnReady-Panic caught and loggedNoYes
OnShutdownLIFOErrors ignoredYes (shutdown timeout)No
OnStop-Panic caught and loggedNoNo
OnRouteSequential-NoNo

Example

a := app.MustNew()

// OnStart: Initialize (sequential, stops on error)
a.OnStart(func(ctx context.Context) error {
    return db.Connect(ctx)
})

// OnReady: Post-startup (async, non-blocking)
a.OnReady(func() {
    consul.Register("my-service", ":8080")
})

// OnShutdown: Graceful cleanup (LIFO, with timeout)
a.OnShutdown(func(ctx context.Context) {
    db.Close()
})

// OnStop: Final cleanup (best-effort)
a.OnStop(func() {
    cleanupTempFiles()
})

9 - Troubleshooting

Common issues and solutions for the App package.

Configuration Errors

Validation Errors

Problem: app.New() returns validation errors.

Solution: Check error message for specific field. Common issues:

  • Empty service name or version.
  • Invalid environment. Must be “development” or “production”.
  • ReadTimeout greater than WriteTimeout.
  • ShutdownTimeout less than 1 second.
  • MaxHeaderBytes less than 1KB.

Example:

a, err := app.New(
    app.WithServiceName(""),  // ❌ Empty
)
// Error: "serviceName must not be empty"

Import Errors

Problem: Cannot import rivaas.dev/app.

Solution:

go get rivaas.dev/app
go mod tidy

Ensure Go 1.25+ is installed.

Server Issues

Port Already in Use

Problem: Server fails to start with “address already in use”.

Solution: Check if port is in use:

lsof -i :8080
# Or
netstat -an | grep 8080

Kill the process or use a different port.

Routes Not Registering

Problem: Routes return 404 even though registered.

Solution:

  • Ensure routes registered before Start().
  • Check paths match exactly. They are case-sensitive.
  • Verify HTTP method matches.
  • Router freezes on startup. Can’t add routes after.

Graceful Shutdown Not Working

Problem: Server doesn’t shut down cleanly.

Solution:

  • Increase shutdown timeout: WithShutdownTimeout(60 * time.Second).
  • Check OnShutdown hooks complete quickly.
  • Verify handlers respect context cancellation.

Observability Issues

Metrics Not Appearing

Problem: Metrics endpoint returns 404.

Solution:

  • Ensure metrics enabled: WithMetrics()
  • Check metrics address: a.GetMetricsServerAddress()
  • Default is separate server on :9090/metrics
  • Use WithMetricsOnMainRouter("/metrics") to mount on main router

Tracing Not Working

Problem: No traces appear in backend.

Solution:

  • Verify tracing enabled: WithTracing()
  • Check OTLP endpoint configuration
  • Ensure tracing backend is running and accessible
  • Verify network connectivity
  • Check logs for tracing initialization errors

Logs Not Appearing

Problem: No logs are written.

Solution:

  • Ensure logging enabled: WithLogging()
  • Check log level configuration
  • Verify logger handler is correct (JSON, Console, etc.)
  • Use c.Logger() in handlers, not package-level logger

Middleware Issues

Middleware Not Executing

Problem: Middleware functions aren’t being called.

Solution:

  • Ensure middleware added before routes
  • Check middleware calls c.Next()
  • Verify middleware isn’t returning early
  • Default recovery middleware is included automatically

Authentication Failing

Problem: Auth middleware not working correctly.

Solution:

  • Check header/token extraction logic
  • Verify middleware order (auth should run early)
  • Ensure c.Next() is called on success
  • Test middleware in isolation

Testing Issues

Test Hangs

Problem: a.Test() never returns.

Solution:

  • Set timeout: a.Test(req, app.WithTimeout(5*time.Second))
  • Check for infinite loops in handler
  • Verify middleware calls c.Next()

Test Fails with Panic

Problem: Test panics instead of returning error.

Solution:

  • Use recover() in test or
  • Check that handler doesn’t panic
  • Recovery middleware catches panics in real server

Health Check Issues

Health Checks Always Failing

Problem: /healthz or /readyz always returns 503.

Solution:

  • Check health check functions return nil on success
  • Verify dependencies (database, cache) are accessible
  • Check health timeout is sufficient
  • Test health checks independently

Health Checks Never Complete

Problem: Health checks timeout.

Solution:

  • Increase timeout: WithHealthTimeout(2 * time.Second)
  • Check dependencies respond within timeout
  • Verify no deadlocks in check functions
  • Use context timeout in check functions

Debugging Tips

Enable Development Mode

app.WithEnvironment("development")

Enables verbose logging and route table display.

Check Observability Status

if a.Metrics() != nil {
    fmt.Println("Metrics:", a.GetMetricsServerAddress())
}
if a.Tracing() != nil {
    fmt.Println("Tracing enabled")
}

Use Test Helpers

resp, err := a.Test(req)  // Test without starting server

Enable GC Tracing

GODEBUG=gctrace=1 go run main.go

Getting Help