server

package
v2.8.3 Latest Latest
Warning

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

Go to latest
Published: Feb 26, 2026 License: MIT Imports: 11 Imported by: 0

README

HTTP Server Package (v2)

A clean, production-ready HTTP server implementation using the Echo framework with built-in health checks and graceful shutdown.

Note: Start and StartWithConfig return error instead of calling os.Exit(1). Callers must handle the returned error. See examples below.

Quick Start

Get your server up and running with minimal configuration:

package main

import (
    "github.com/jasoet/pkg/v2/server"
    "github.com/labstack/echo/v4"
)

func main() {
    // Define what happens when server starts
    operation := func(e *echo.Echo) {
        // Register your routes here
        e.GET("/hello", func(c echo.Context) error {
            return c.String(200, "Hello, World!")
        })
    }

    // Define cleanup actions when server stops
    shutdown := func(e *echo.Echo) {
        // Cleanup resources here
    }

    // Start server on port 8080
    if err := server.Start(8080, operation, shutdown); err != nil {
        log.Fatal().Err(err).Msg("server failed")
    }
}

Configuration Options

The server can be customized using the Config struct:

Field Type Description Default
Port int The port number to listen on -
Operation func(e *echo.Echo) Function to run when server starts -
Shutdown func(e *echo.Echo) Function to run when server stops -
Middleware []echo.MiddlewareFunc Custom middleware to apply []
ShutdownTimeout time.Duration Timeout for graceful shutdown 10s
EchoConfigurer func(e *echo.Echo) Function to configure Echo instance nil

Example with custom configuration:

config := server.DefaultConfig(8080, operation, shutdown)
config.ShutdownTimeout = 30 * time.Second
if err := server.StartWithConfig(config); err != nil {
    log.Fatal().Err(err).Msg("server failed")
}
Using EchoConfigurer

The EchoConfigurer allows you to configure the Echo instance directly after it's created but before the server starts. This is useful for Echo-specific configurations like custom error handlers, validators, or other Echo settings.

config := server.DefaultConfig(8080, operation, shutdown)

// Configure Echo instance
config.EchoConfigurer = func(e *echo.Echo) {
    // Custom error handler
    e.HTTPErrorHandler = myCustomErrorHandler

    // Custom validator
    e.Validator = myValidator

    // Other Echo-specific configurations
    e.Debug = true
}

if err := server.StartWithConfig(config); err != nil {
    log.Fatal().Err(err).Msg("server failed")
}

Middleware Examples

Adding Custom Middleware
package main

import (
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
    "github.com/jasoet/pkg/v2/server"
)

func main() {
    operation := func(e *echo.Echo) {
        // Your routes here
    }

    shutdown := func(e *echo.Echo) {
        // Your cleanup here
    }

    // Add custom middleware
    corsMiddleware := middleware.CORSWithConfig(middleware.CORSConfig{
        AllowOrigins: []string{"https://example.com"},
        AllowMethods: []string{echo.GET, echo.PUT, echo.POST, echo.DELETE},
    })

    rateLimiter := middleware.RateLimiterWithConfig(middleware.RateLimiterConfig{
        Skipper: middleware.DefaultSkipper,
        Store:   middleware.NewRateLimiterMemoryStore(20),
    })

    // Start server with middleware
    if err := server.Start(8080, operation, shutdown, corsMiddleware, rateLimiter); err != nil {
        log.Fatal().Err(err).Msg("server failed")
    }
}
Creating Your Own Middleware
package main

import (
    "fmt"
    "github.com/labstack/echo/v4"
    "github.com/jasoet/pkg/v2/server"
    "time"
)

func main() {
    // Create custom timing middleware
    timingMiddleware := func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            start := time.Now()

            // Execute the next handler
            err := next(c)

            // Log the time taken
            duration := time.Since(start)
            fmt.Printf("[%s] %s - %v\n",
                c.Request().Method,
                c.Path(),
                duration)

            return err
        }
    }

    // Start server with custom middleware
    if err := server.Start(8080,
        func(e *echo.Echo) {},
        func(e *echo.Echo) {},
        timingMiddleware); err != nil {
        log.Fatal().Err(err).Msg("server failed")
    }
}

Health Checks

The server includes built-in health check endpoints:

Endpoint Description Response
/health General health status {"status":"UP"}
/health/ready Readiness check {"status":"READY"}
/health/live Liveness check {"status":"ALIVE"}
Customizing Health Checks

You can customize the health check endpoints in your operation function:

operation := func(e *echo.Echo) {
    // Override the default health endpoint
    e.GET("/health", func(c echo.Context) error {
        // Check your application's health
        dbHealthy := checkDatabaseConnection()
        cacheHealthy := checkCacheConnection()

        if !dbHealthy || !cacheHealthy {
            return c.JSON(500, map[string]interface{}{
                "status": "DOWN",
                "components": map[string]string{
                    "database": dbHealthy ? "UP" : "DOWN",
                    "cache": cacheHealthy ? "UP" : "DOWN",
                },
            })
        }

        return c.JSON(200, map[string]interface{}{
            "status": "UP",
            "components": map[string]string{
                "database": "UP",
                "cache": "UP",
            },
        })
    })
}

Graceful Shutdown

The server supports graceful shutdown, allowing in-flight requests to complete before shutting down.

Basic Shutdown Handler
shutdown := func(e *echo.Echo) {
    // Close database connections
    db.Close()

    // Close message queue connections
    mq.Close()

    fmt.Println("All resources have been properly released")
}
Advanced Shutdown with Context
package main

import (
    "context"
    "fmt"
    "github.com/labstack/echo/v4"
    "github.com/jasoet/pkg/v2/server"
    "time"
)

func main() {
    // Create resources with context
    ctx, cancel := context.WithCancel(context.Background())

    // Start background workers
    worker := startBackgroundWorker(ctx)

    shutdown := func(e *echo.Echo) {
        fmt.Println("Shutting down background workers...")

        // Signal workers to stop
        cancel()

        // Wait for worker to finish with timeout
        select {
        case <-worker.Done():
            fmt.Println("Worker shutdown completed")
        case <-time.After(5 * time.Second):
            fmt.Println("Worker shutdown timed out")
        }

        fmt.Println("Shutdown complete")
    }

    // Configure server with longer shutdown timeout
    config := server.DefaultConfig(8080, func(e *echo.Echo) {}, shutdown)
    config.ShutdownTimeout = 30 * time.Second

    if err := server.StartWithConfig(config); err != nil {
        log.Fatal().Err(err).Msg("server failed")
    }
}

Advanced Usage

Integrating with Existing Applications
package main

import (
    "github.com/labstack/echo/v4"
    "github.com/jasoet/pkg/v2/server"
    "your-module/auth"
    "your-module/database"
)

func main() {
    // Initialize your application components
    db := database.New()
    authService := auth.New(db)

    // Create your API handlers
    userHandler := NewUserHandler(db, authService)
    productHandler := NewProductHandler(db)

    // Define server operation
    operation := func(e *echo.Echo) {
        // Group routes by API version
        v1 := e.Group("/api/v1")

        // User routes
        v1.POST("/users", userHandler.Create)
        v1.GET("/users/:id", userHandler.Get)
        v1.PUT("/users/:id", userHandler.Update)
        v1.DELETE("/users/:id", userHandler.Delete)

        // Product routes
        v1.GET("/products", productHandler.List)
        v1.GET("/products/:id", productHandler.Get)

        // Add authentication middleware to protected routes
        admin := v1.Group("/admin")
        admin.Use(authService.AdminMiddleware)
        admin.GET("/stats", productHandler.Stats)
    }

    // Define shutdown
    shutdown := func(e *echo.Echo) {
        db.Close()
    }

    // Start the server
    if err := server.Start(8080, operation, shutdown); err != nil {
        log.Fatal().Err(err).Msg("server failed")
    }
}
Custom Error Handling
package main

import (
    "fmt"
    "github.com/labstack/echo/v4"
    "net/http"
    "time"
    "github.com/jasoet/pkg/v2/server"
)

func main() {
    // Define your custom error handler
    customErrorHandler := func(err error, c echo.Context) {
        code := http.StatusInternalServerError
        message := "Internal Server Error"

        if he, ok := err.(*echo.HTTPError); ok {
            code = he.Code
            message = fmt.Sprintf("%v", he.Message)
        }

        // Log the error
        fmt.Printf("Error: %v\n", err)

        // Return a custom error response
        c.JSON(code, map[string]interface{}{
            "error": message,
            "status": code,
            "timestamp": time.Now().Format(time.RFC3339),
        })
    }

    // Define operation for business logic
    operation := func(e *echo.Echo) {
        // Register your routes here
        e.GET("/api/users", listUsers)
    }

    // Define shutdown function
    shutdown := func(e *echo.Echo) {
        // Cleanup resources
    }

    // Create config with EchoConfigurer
    config := server.DefaultConfig(8080, operation, shutdown)

    // Set Echo-specific configurations
    config.EchoConfigurer = func(e *echo.Echo) {
        // Set custom error handler
        e.HTTPErrorHandler = customErrorHandler

        // Other Echo configurations
        e.Debug = true
        e.Validator = myCustomValidator
    }

    // Start the server
    if err := server.StartWithConfig(config); err != nil {
        log.Fatal().Err(err).Msg("server failed")
    }
}

Examples

For complete, runnable examples, see the examples directory.

The examples demonstrate:

  • Basic server setup
  • Custom routes and middleware
  • Health check implementations
  • Graceful shutdown patterns

Run the examples:

cd examples
go run -tags example example.go

Best Practices

1. Always Use Operation and Shutdown Functions
// Good
operation := func(e *echo.Echo) {
    // Register routes
    e.GET("/users", getUsersHandler)
}

shutdown := func(e *echo.Echo) {
    // Cleanup resources
    db.Close()
}

if err := server.Start(8080, operation, shutdown); err != nil {
    log.Fatal().Err(err).Msg("server failed")
}
2. Use Middleware for Cross-Cutting Concerns
// Apply middleware for auth, logging, rate limiting, etc.
authMiddleware := createAuthMiddleware()
rateLimiter := middleware.RateLimiterWithConfig(...)

if err := server.Start(8080, operation, shutdown, authMiddleware, rateLimiter); err != nil {
    log.Fatal().Err(err).Msg("server failed")
}
3. Implement Proper Health Checks
operation := func(e *echo.Echo) {
    e.GET("/health", func(c echo.Context) error {
        // Check dependencies
        if !db.Ping() {
            return c.JSON(503, map[string]string{
                "status": "DOWN",
                "reason": "database unavailable",
            })
        }
        return c.JSON(200, map[string]string{"status": "UP"})
    })
}

API Reference

Functions
Start(port int, operation Operation, shutdown Shutdown, middleware ...echo.MiddlewareFunc) error

Starts the HTTP server with simplified configuration. Returns an error if the server fails to start or shut down.

StartWithConfig(config Config) error

Starts the HTTP server with the given configuration. Returns an error if the server fails to start or shut down.

DefaultConfig(port int, operation Operation, shutdown Shutdown) Config

Returns a default server configuration.

Types
Operation func(e *echo.Echo)

Function to execute when server starts. Use this to register routes and handlers.

Shutdown func(e *echo.Echo)

Function to execute when server stops. Use this for cleanup.

EchoConfigurer func(e *echo.Echo)

Function to configure the Echo instance directly.

Troubleshooting

Server won't start
  • Check if the port is already in use
  • Verify Operation function doesn't have errors
  • Check for panics in route handlers
Graceful shutdown timeout
  • Increase ShutdownTimeout in config
  • Check for long-running operations in handlers
  • Ensure Shutdown function completes quickly

License

This package is part of github.com/jasoet/pkg and follows the repository's license.

Documentation

Overview

Package server provides a lifecycle-managed HTTP server built on Echo, with health endpoints, graceful shutdown, and optional OTel integration.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Start

func Start(port int, operation Operation, shutdown Shutdown, middleware ...echo.MiddlewareFunc) error

Start starts the HTTP server with simplified configuration.

func StartWithConfig

func StartWithConfig(config Config) error

StartWithConfig starts the HTTP server with the given configuration and blocks until an OS interrupt signal is received, then shuts down gracefully.

Types

type Config

type Config struct {
	Port int

	Operation Operation

	Shutdown Shutdown

	Middleware []echo.MiddlewareFunc

	ShutdownTimeout time.Duration

	EchoConfigurer EchoConfigurer

	OTelConfig *otel.Config `yaml:"-" mapstructure:"-"`
}

Config holds the HTTP server configuration.

func DefaultConfig

func DefaultConfig(port int, operation Operation, shutdown Shutdown) Config

DefaultConfig returns a default server configuration.

func NewConfig added in v2.7.14

func NewConfig(opts ...Option) Config

NewConfig creates a Config using functional options with sensible defaults.

type EchoConfigurer

type EchoConfigurer func(e *echo.Echo)

EchoConfigurer is called during setup to customise the Echo instance (add routes, middleware, etc.).

type Operation

type Operation func(e *echo.Echo)

Operation is called after the Echo instance is configured but before it starts listening.

type Option added in v2.7.14

type Option func(*Config)

Option configures a Config during construction.

func WithEchoConfigurer added in v2.7.14

func WithEchoConfigurer(ec EchoConfigurer) Option

WithEchoConfigurer sets a callback that customises the Echo instance.

func WithMiddleware added in v2.7.14

func WithMiddleware(m ...echo.MiddlewareFunc) Option

WithMiddleware appends Echo middleware to the chain.

func WithOTelConfig added in v2.7.14

func WithOTelConfig(cfg *otel.Config) Option

WithOTelConfig sets the OpenTelemetry configuration.

func WithOperation added in v2.7.14

func WithOperation(op Operation) Option

WithOperation sets the Operation callback.

func WithPort added in v2.7.14

func WithPort(port int) Option

WithPort sets the server listen port.

func WithShutdown added in v2.7.14

func WithShutdown(s Shutdown) Option

WithShutdown sets the Shutdown callback.

func WithShutdownTimeout added in v2.7.14

func WithShutdownTimeout(d time.Duration) Option

WithShutdownTimeout sets the graceful-shutdown deadline.

type Shutdown

type Shutdown func(e *echo.Echo)

Shutdown is called during graceful shutdown before the Echo instance is stopped.

Jump to

Keyboard shortcuts

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