A lightweight, production-ready, zero-dependency scaffold for building high-performance HTTP services in Go. This project emphasizes standard library utilization to ensure long-term stability and minimal supply chain risk.
go-serve adheres to the Standard Go Project Layout with a simplified Clean Architecture approach, prioritizing minimalism, extensibility, and observability. This structure facilitates clear separation of concerns and maintainable code, making it easy to extend for various service needs.
-
cmd/: Contains the main application entry points.
cmd/api/main.go: The primary executable for the HTTP API service, responsible for bootstrapping the application, loading configuration, initializing the server, and handling graceful shutdown.
-
internal/: Houses private application logic that is not intended for public consumption by other Go projects. This prevents other projects from importing these internal packages, enforcing a clear boundary.
-
internal/config/: Manages environment variable parsing and application configuration using only the standard library'sospackage. It ensures critical configurations are present or sensible defaults are applied. -
internal/server/: Encapsulates the HTTP server setup, including route definitions, server instance creation, and the graceful shutdown mechanism usingos/signalandcontext.WithTimeout. -
internal/middleware/: Provides custom standard library HTTP middleware for cross-cutting concerns such as structured logging (log/slog), request ID generation, and panic recovery. -
internal/handlers/: Contains the HTTP handler functions that implement the application's business logic. These handlers are designed to be "zero global state," meaning configuration and logger instances are injected into the server and then passed to handlers, promoting testability and clarity.
-
-
pkg/: Intended for public library code that can be safely used by external applications or other projects within a larger monorepo. (Currently empty, as per the minimalist philosophy, but available for future expansion).
-
Standard Library First:
go-serveis built almost exclusively on Go's standard library (net/http,log/slog,os,context,os/signal, etc.), minimizing external dependencies and supply chain risk. -
Zero Global State: Configuration and
log/sloglogger instances are explicitly injected into theServerstruct and subsequently passed down to middleware and handlers. This design choice enhances testability, reduces side effects, and improves code clarity. -
Extensibility as a Blank Canvas: The template provides a robust foundation without imposing specific database, message queue, or external client choices. Developers are guided on where to integrate such components (e.g., in
internal/storefor database logic). -
Observability Built-in: Structured JSON logging (
log/slog) and metrics-readiness are fundamental components, integrated via middleware, to provide immediate insights into application behavior. -
Graceful Shutdown: The server is configured to handle
SIGINTandSIGTERMsignals, allowing for a controlled shutdown to complete ongoing requests and release resources, ensuring data integrity during deployments or restarts.
The following diagram illustrates the typical request lifecycle and architectural components:
sequenceDiagram
participant Client
participant Main as cmd/api/main.go
participant Config as internal/config
participant Server as internal/server
participant Middleware as internal/middleware
participant Router as net/http.ServeMux
participant Handler as internal/handlers
rect rgb(238, 255, 238)
note over Client,Handler: Application Lifecycle
Client->>Main: Start Application / Incoming Request
activate Main
Main->>Config: Load Configuration (Env Vars)
activate Config
Config-->>Main: App Config
deactivate Config
Main->>Server: Initialize HTTP Server (Config, Logger)
activate Server
Server->>Middleware: Apply Global Middleware Chain (RequestID, Logger, Recovery)
activate Middleware
Middleware->>Router: Register Routes
deactivate Middleware
note over Server: Server Listening
deactivate Server
Client->>Server: HTTP Request
activate Server
Server->>Middleware: Process Request
activate Middleware
Middleware->>Router: Route Request
activate Router
Router->>Handler: Execute Handler Logic
activate Handler
Handler-->>Router: HTTP Response (JSON)
deactivate Handler
Router-->>Middleware: Forward Response
deactivate Router
Middleware-->>Server: Send Response
deactivate Middleware
Server-->>Client: HTTP Response
deactivate Server
note over Main: Application Running
Main->>Main: OS Signal (SIGINT/SIGTERM)
Main->>Server: Initiate Graceful Shutdown
activate Server
Server-->>Main: Server Stopped
deactivate Server
deactivate Main
end
To start a new project using go-serve as a template, it's recommended to clone the latest release tag to get a clean, stable starting point.
# Replace v1.0.0 with the desired release tag
git clone --depth 1 --branch v1.0.0 https://github.com/maranix/go-serve.git my-new-project
cd my-new-project
# Remove the .git directory to start a fresh Git history
rm -rf .git
# Initialize your new Git repository
git init
git add .
git commit -m "feat: initial commit from go-serve template"For an even quicker setup in an empty or newly initialized Git repository, you can use the setup-template.sh script:
-
Download the script:
curl -LJO https://raw.githubusercontent.com/maranix/go-serve/main/setup-template.sh chmod +x setup-template.sh
(Note: Replace
mainwithv1.0.0or your desired release branch/tag if you want to use a specific version of the script.) -
Run the script:
./setup-template.sh
This script will automatically clone the template, copy the files, and perform an initial Git commit for you.
- Go 1.25+
To run the server locally:
make runThe server will start on the port specified by the PORT environment variable (defaults to 8080).
To run the test suite:
make testTo check the code for potential issues:
make lintTo build the Docker image:
make docker-buildTo build and run the Docker container (it will be removed automatically on exit):
make docker-runThe server inside the container will be accessible on port 8080 of your host machine.
go-serve is designed to be a blank canvas. Here’s how to add a database connection.
-
Create a Store Package: Create a new package under
internal/store. This package will be responsible for all database interactions.// internal/store/db.go package store import "database/sql" type Store struct { db *sql.DB } func New(db *sql.DB) *Store { return &Store{db: db} } // Add your database methods here... func (s *Store) GetUser(id string) (*User, error) { ... }
-
Instantiate in
main.go: Incmd/api/main.go, instantiate your database connection and pass it to your new store.func main() { // ... (config and logger setup) // Example for PostgreSQL connStr := "user=... password=... dbname=... sslmode=disable" db, err := sql.Open("postgres", connStr) if err != nil { logger.Error("failed to connect to database", "error", err) os.Exit(1) } dbStore := store.New(db) // ... }
-
Inject into Handlers: Update your
server.Serverstruct to hold the store, and pass it down to your handlers.First, update
internal/server/server.go:type Server struct { //... store *store.Store } // Update New() to accept the store func New(store *store.Store, ...) *Server { ... s := &Server{ ... store: store, }
Next, modify your handlers to be methods on a handler struct that holds the store.
// Create a handler struct type Handlers struct { store *store.Store logger *slog.Logger } // Example handler method func (h *Handlers) HandleGetUser(w http.ResponseWriter, r *http.Request) { user, err := h.store.GetUser(...) // ... } // In server.routes() func (s *Server) routes() { handlers := &Handlers{logger: s.logger} s.mux.HandleFunc("GET /users/{id}", handlers.HandleGetUser) }
This example demonstrates how to integrate a database into your
go-serveapplication. Remember to adapt the connection string and repository logic to your specific database and application needs.
Contributions to go-serve are welcome!
Please see our CONTRIBUTING.md for details on how to get started, our code of conduct, and submission process.
This project is licensed under the MIT License
See the LICENSE file for details.