Configuration

Configure your Rivaas application

Overview

Rivaas uses the functional options pattern for configuration. This provides a clean, self-documenting API. It’s backward-compatible. This guide covers basic configuration options.

💡 First Time? Focus on sections marked with ⭐. Skip advanced topics for now.

Configuration Philosophy

  • Sensible Defaults: Works out of the box.
  • Progressive Disclosure: Start simple. Add complexity as needed.
  • Type Safety: Configuration errors are caught at startup.
  • Environment Aware: Different defaults for dev and prod.

⭐ Basic Configuration

Service Metadata

Set your service name and version:

a := app.MustNew(
    app.WithServiceName("my-api"),
    app.WithServiceVersion("v1.2.3"),
    app.WithEnvironment("production"),
)

These values are sent to all observability components. This includes logging, metrics, and tracing.

Advanced: Server Configuration

⚠️ Advanced Topic: Most applications don’t need custom server configuration. The defaults work for production.

Timeouts

Configure server timeouts to protect against slow clients:

a := app.MustNew(
    app.WithServiceName("my-api"),
    app.WithServerConfig(
        app.WithReadTimeout(10 * time.Second),
        app.WithWriteTimeout(15 * time.Second),
        app.WithIdleTimeout(60 * time.Second),
        app.WithShutdownTimeout(30 * time.Second),
    ),
)

Timeout Options:

OptionDescriptionDefault
WithReadTimeoutMaximum time to read request10s
WithWriteTimeoutMaximum time to write response10s
WithIdleTimeoutMaximum idle connection time60s
WithReadHeaderTimeoutMaximum time to read headers2s
WithShutdownTimeoutGraceful shutdown timeout30s

Request Limits

Configure request size limits:

app.WithServerConfig(
    app.WithMaxHeaderBytes(1 << 20), // 1MB
)

⭐ Environment Modes

Rivaas supports two environment modes with different defaults:

Development Mode (Default)

a := app.MustNew(
    app.WithEnvironment("development"),
)

Features:

  • Verbose logging enabled
  • Access logging for all requests
  • Development-friendly error messages
  • Pretty-printed JSON logs

Production Mode

a := app.MustNew(
    app.WithEnvironment("production"),
)

Features:

  • Error-only logging
  • JSON structured logs
  • Minimal overhead
  • Production-ready defaults

Observability

💡 Note: This section covers observability setup. For detailed observability patterns, see the Observability Guide.

Enable logging, metrics, and tracing:

import (
    "rivaas.dev/app"
    "rivaas.dev/logging"
    "rivaas.dev/metrics"
    "rivaas.dev/tracing"
)

a := app.MustNew(
    app.WithServiceName("my-api"),
    app.WithObservability(
        app.WithLogging(logging.WithJSONHandler()),
        app.WithMetrics(),
        app.WithTracing(tracing.WithStdout()),
    ),
)

Logging Options

app.WithLogging(
    logging.WithJSONHandler(),              // JSON output
    logging.WithLevel(logging.LevelInfo),   // Log level
)

// Or use console handler (development)
app.WithLogging(logging.WithConsoleHandler())

Metrics Options

// Prometheus metrics (default)
app.WithMetrics()

// Custom Prometheus endpoint
app.WithMetrics(
    metrics.WithPrometheus(":9090", "/metrics"),
)

// OTLP metrics
app.WithMetrics(
    metrics.WithOTLP("localhost:4317"),
)

Tracing Options

// Stdout tracing (development)
app.WithTracing(tracing.WithStdout())

// OTLP tracing (production)
app.WithTracing(
    tracing.WithOTLP("jaeger:4317"),
    tracing.WithSampleRate(0.1), // Sample 10% of requests
)

Exclude Paths from Observability

Exclude health checks and static paths from logging/metrics/tracing:

app.WithObservability(
    app.WithLogging(),
    app.WithMetrics(),
    app.WithTracing(),
    app.WithExcludePaths("/livez", "/readyz", "/metrics"),
    app.WithExcludePrefixes("/static", "/assets"),
)

⭐ Health Endpoints

Add Kubernetes-compatible health endpoints:

a := app.MustNew(
    app.WithServiceName("my-api"),
    app.WithHealthEndpoints(
        app.WithLivenessCheck("process", func(ctx context.Context) error {
            return nil // Process is alive
        }),
        app.WithReadinessCheck("database", func(ctx context.Context) error {
            return db.PingContext(ctx) // Check DB connection
        }),
    ),
)

This registers:

  • GET /livez — Liveness probe
  • GET /readyz — Readiness probe

Custom Health Paths

app.WithHealthEndpoints(
    app.WithHealthPrefix("/_system"),       // Prefix: /_system/livez
    app.WithLivezPath("/live"),             // Custom path: /_system/live
    app.WithReadyzPath("/ready"),           // Custom path: /_system/ready
    app.WithHealthTimeout(500 * time.Millisecond),
)

Advanced: Debug Endpoints

⚠️ Security Critical: Only enable pprof in controlled environments.

Enable pprof for profiling (use with caution):

// Enable conditionally (recommended for production)
app.WithDebugEndpoints(
    app.WithPprofIf(os.Getenv("PPROF_ENABLED") == "true"),
)

// Always enable (development only)
app.WithDebugEndpoints(
    app.WithPprof(),
)

⚠️ Security Warning: Never expose pprof endpoints in production without proper authentication.

Advanced: Middleware Configuration

Add middleware during initialization or after app creation:

import (
    "rivaas.dev/router/middleware/cors"
    "rivaas.dev/router/middleware/requestid"
)

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

// Add middleware after creation
a.Use(requestid.New())
a.Use(cors.New(
    cors.WithAllowedOrigins([]string{"https://example.com"}),
))

Or during initialization:

a := app.MustNew(
    app.WithServiceName("my-api"),
    app.WithMiddleware(
        requestid.New(),
        cors.New(cors.WithAllowAllOrigins(true)),
    ),
)

💡 Learn More: See the Middleware Guide for detailed middleware usage patterns.

Complete Example

Here’s a production-ready configuration:

package main

import (
    "context"
    "log"
    "os"
    "time"

    "rivaas.dev/app"
    "rivaas.dev/logging"
    "rivaas.dev/metrics"
    "rivaas.dev/tracing"
    "rivaas.dev/router/middleware/cors"
    "rivaas.dev/router/middleware/requestid"
)

func main() {
    a := app.MustNew(
        // Service metadata
        app.WithServiceName("my-api"),
        app.WithServiceVersion("v1.0.0"),
        app.WithEnvironment("production"),

        // Server configuration
        app.WithServerConfig(
            app.WithReadTimeout(10 * time.Second),
            app.WithWriteTimeout(15 * time.Second),
            app.WithShutdownTimeout(30 * time.Second),
        ),

        // Observability
        app.WithObservability(
            app.WithLogging(logging.WithJSONHandler()),
            app.WithMetrics(metrics.WithPrometheus(":9090", "/metrics")),
            app.WithTracing(tracing.WithOTLP("jaeger:4317")),
            app.WithExcludePaths("/livez", "/readyz", "/metrics"),
        ),

        // Health checks
        app.WithHealthEndpoints(
            app.WithReadinessCheck("database", checkDatabase),
        ),

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

    // Add middleware
    a.Use(requestid.New())
    a.Use(cors.New(cors.WithAllowedOrigins([]string{
        "https://example.com",
    })))

    // Register routes
    a.GET("/", handleRoot)

    // Start server
    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
    defer cancel()

    if err := a.Start(ctx); err != nil {
        log.Fatal(err)
    }
}

func checkDatabase(ctx context.Context) error {
    // Implement your database check
    return nil
}

func handleRoot(c *app.Context) {
    c.JSON(200, map[string]string{"status": "ok"})
}

Advanced: Configuration Validation

Rivaas validates configuration at startup and returns clear errors:

a, err := app.New(
    app.WithServerConfig(
        app.WithReadTimeout(15 * time.Second),
        app.WithWriteTimeout(10 * time.Second), // ❌ Read > Write
    ),
)
if err != nil {
    // Error: "server.readTimeout: read timeout should not exceed write timeout"
}

Advanced: Environment Variables

While Rivaas doesn’t directly use environment variables, you can easily integrate them:

import "os"

func getEnv(key, fallback string) string {
    if value := os.Getenv(key); value != "" {
        return value
    }
    return fallback
}

a := app.MustNew(
    app.WithServiceName(getEnv("SERVICE_NAME", "my-api")),
    app.WithServiceVersion(getEnv("SERVICE_VERSION", "v1.0.0")),
    app.WithEnvironment(getEnv("ENVIRONMENT", "development")),
)

Next Steps

Now that you understand configuration, explore these topics:

Reference

For a complete list of all configuration options, see the App Options Reference.