goforge

package module
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Jul 27, 2026 License: MIT Imports: 8 Imported by: 0

README

goforge

Go Reference

goforge is a collection of small, composable Go modules for building HTTP services with the standard library. It standardizes recurring service concerns without introducing a web framework or hiding the underlying Go APIs.

The repository is organized around two primary entry points:

  • chassis builds the inbound HTTP stack from http.ServeMux and opt-in middleware.
  • httpclient builds the outbound HTTP stack from http.Client, composable transports, and a typed JSON executor.

The otel module configures the traces, metrics, logs, propagation, and runtime instrumentation shared by both HTTP stacks.

The root module contains the contracts and primitives shared by those modules: structured errors, JSON responses, context-scoped loggers, and endpoint registration interfaces.

Modules

Each directory containing a go.mod is an independently versioned Go module. Applications only need to depend on the pieces they use.

Module Purpose Status
goforge Shared errors, responses, contexts, and contracts Available
chassis Middleware-aware http.ServeMux Available
httpmiddlewares Standard net/http middleware Available
httpclient Configured HTTP clients and typed JSON calls Available
otel Explicit OpenTelemetry traces, metrics, logs, and lifecycle Available
forgemongo Typed MongoDB stores and mocks Available
forgesentry slog integration for Sentry Available
linters Shared lint rules Scaffold

Install

Install each module independently:

go get github.com/lgosse/goforge@latest
go get github.com/lgosse/goforge/chassis@latest
go get github.com/lgosse/goforge/httpclient@latest
go get github.com/lgosse/goforge/otel@latest

Building an HTTP service

chassis.NewServeMux behaves like a plain http.ServeMux when called without options. Services can opt into the standard GoForge stack or select individual middleware:

telemetryConfig := forgeotel.DefaultConfig("users-api", localDevelopment)
telemetryConfig.OTLP.Endpoint = "otel-collector.internal:4317" // Production only.
telemetry, err := forgeotel.New(ctx, telemetryConfig)
if err != nil {
	return err
}
defer telemetry.Shutdown(context.Background())

mux := chassis.NewServeMux(
	chassis.WithDefaultChassis(),
	chassis.WithOpenTelemetry(telemetry.HTTPServerOptions()...),
	chassis.WithLogger(telemetry.Logger()),
	chassis.WithCORS(httpmiddlewares.CORSConfig{
		AllowedOrigins: []string{"https://app.example.com"},
	}),
)

mux.HandleFunc("GET /health", func(w http.ResponseWriter, _ *http.Request) {
	_ = goforge.RespondJSON(w, map[string]bool{"healthy": true}, http.StatusOK)
})

server := &http.Server{
	Addr:    ":8080",
	Handler: mux,
}

log.Fatal(server.ListenAndServe())

See the chassis README for middleware ordering, route scoping, and a fuller server example.

Calling another service

httpclient.NewClient configures a high-throughput standard HTTP client. Transport options wrap one another, allowing authentication and telemetry to be composed:

client := httpclient.NewClient(
	httpclient.WithTimeout(10*time.Second),
	httpclient.WithAPIKey("X-API-Key", os.Getenv("SERVICE_API_KEY")),
	httpclient.WithTelemetry(telemetry.HTTPClientOptions()...),
)

user, err := httpclient.Call[User](
	ctx,
	client,
	http.MethodGet,
	"https://users.internal",
	"/v1/users/user-1",
	nil,
	nil,
)
if err != nil {
	return err
}

fmt.Println(user.ID)

See the httpclient README for OAuth, request options, error mapping, and transport composition.

Versioning

The root module uses repository tags such as v0.3.0. Nested modules use tags prefixed by their directory:

chassis/v0.3.0
httpclient/v0.3.0
forgemongo/v0.3.0
otel/v0.3.0

Releasing one module does not require releasing every module.

Development

Because this is a multi-module repository, root tests do not traverse nested modules. Run checks from the module being changed:

cd httpclient
go test -race ./...
go vet ./...

License

GoForge is available under the MIT License.

Documentation

Overview

Package goforge defines the shared contracts and primitives used throughout the GoForge modules.

It keeps service-facing concerns such as structured errors, JSON responses, context-scoped logging, and endpoint registration consistent without imposing an application framework. Specialized integrations live in independently versioned modules.

A typical service uses chassis as its inbound HTTP foundation. chassis.NewServeMux starts with the standard library's routing behavior and adds only the logging, recovery, tracing, authentication, CORS, caching, or application middleware the service selects. The httpmiddlewares module exposes those building blocks directly when a service does not need the chassis abstraction.

For outbound HTTP, httpclient.NewClient builds a standard http.Client whose transport can be composed with authentication and OpenTelemetry instrumentation. httpclient.Call can then form the typed JSON execution layer beneath small service-specific SDKs.

The otel module supplies the explicit trace, metric, and log providers used by both HTTP foundations. A service normally constructs one otel.Runtime at startup, passes its server options and logger to chassis, passes its client options to httpclient, and shuts the runtime down during graceful termination.

The storage, messaging, error-reporting, and tooling modules complement these inbound and outbound foundations. Each module is independently versioned, so applications can adopt only the integrations they need while sharing the contracts defined by this root package.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func FormatStackTrace

func FormatStackTrace(stack []StackFrame) string

FormatStackTrace formats captured stack frames into a multi-line string.

func LoggerFromContext

func LoggerFromContext(ctx context.Context) *slog.Logger

LoggerFromContext retrieves the logger from the context. If no logger is found, it returns a default logger.

func LoggerFromContextOr added in v0.2.0

func LoggerFromContextOr(ctx context.Context, fallback *slog.Logger) *slog.Logger

LoggerFromContextOr retrieves the logger from the context, falling back to fallback when no logger is present. A nil fallback uses the default logger.

func PtrValue

func PtrValue[T any](p *T) T

func RespondError

func RespondError(w http.ResponseWriter, err error) error

func RespondJSON

func RespondJSON[T any](w http.ResponseWriter, data T, statusCode int) error
Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/lgosse/goforge"
)

func main() {
	recorder := httptest.NewRecorder()

	err := goforge.RespondJSON(recorder, struct {
		ID string `json:"id"`
	}{ID: "user-1"}, http.StatusCreated)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(recorder.Code)
	fmt.Println(recorder.Header().Get("Content-Type"))
	fmt.Println(recorder.Body.String())
}
Output:
201
application/json
{"id":"user-1"}

func WithLogger

func WithLogger(ctx context.Context, logger *slog.Logger) context.Context

WithLogger adds a *log/slog.Logger to the context.

Types

type Endpoint

type Endpoint interface {
	Scheme() string
	Host() string
	Method() string
	Path() string
	Headers() http.Header
}

Endpoint is the standard contract for all goforge endpoints.

type EndpointRegistry

type EndpointRegistry interface {
	Register(endpoint Endpoint) error
	Start() error
}

EndpointRegistry is the standard contract for goforge endpoint registry.

type Error

type Error struct {
	HTTPStatus int          // E.g., 400, 404, 500
	Code       string       // Internal tracking code, e.g., "ERR_USER_FORBIDDEN_PASSWORD_CHANGE", can be used for user-facing translations.
	Message    string       // Safe, user-facing message
	Cause      error        // The underlying wrapped error for logs (should not be exposed to clients)
	Stack      []StackFrame // Captured call stack for logs and error reporters such as Sentry.
}

Error is the standard contract for all goforge errors.

func NewError

func NewError(cause error) *Error

NewError creates a new goforge error with a default 500 status code and the given cause.

Example
package main

import (
	"errors"
	"fmt"
	"net/http"

	"github.com/lgosse/goforge"
)

func main() {
	err := goforge.NewError(errors.New("user does not exist")).
		WithHTTPStatus(http.StatusNotFound).
		WithCode("ERR_USER_NOT_FOUND").
		WithMessage("User not found")

	fmt.Println(err.HTTPStatus, err.Code, err.Message)
}
Output:
404 ERR_USER_NOT_FOUND User not found

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface.

func (*Error) StackTrace

func (e *Error) StackTrace() []StackFrame

StackTrace returns a defensive copy of the captured stack frames.

func (*Error) StackTraceString

func (e *Error) StackTraceString() string

StackTraceString formats the captured stack in the same file:line/function shape most log sinks and error reporters expect.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap allows standard errors.Is and errors.As to work perfectly.

func (*Error) WithCapturedStack

func (e *Error) WithCapturedStack(skip int) *Error

WithCapturedStack captures a fresh stack trace. The skip value excludes additional caller frames above WithCapturedStack.

func (*Error) WithCode

func (e *Error) WithCode(code string) *Error

WithCode sets the internal tracking code for the error.

func (*Error) WithHTTPStatus

func (e *Error) WithHTTPStatus(status int) *Error

WithHTTPStatus sets the HTTP status code for the error.

func (*Error) WithMessage

func (e *Error) WithMessage(message string) *Error

WithMessage sets the user-facing message for the error.

func (*Error) WithStack

func (e *Error) WithStack(stack []StackFrame) *Error

WithStack replaces the captured stack frames.

type StackFrame

type StackFrame struct {
	Function       string
	File           string
	Line           int
	ProgramCounter uintptr
}

StackFrame is a single call frame captured when a goforge error is created.

func CaptureStackTrace

func CaptureStackTrace(skip int) []StackFrame

CaptureStackTrace captures the current goroutine stack. The skip value excludes additional caller frames above CaptureStackTrace.

Directories

Path Synopsis
chassis module
forgemongo module
forgesentry module
httpclient module
otel module
mongo module

Jump to

Keyboard shortcuts

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