Write Elegant Go Handlers by Packing Boilerplate with a Generic Wrap

The article shows how typical Go HTTP/gRPC handlers repeat five steps of decoding, validation, type conversion, business call, and encoding, and demonstrates a generic Wrap adapter that extracts the repetitive pipeline, leaving handlers thin, type‑safe, and focused solely on business logic.

Golang Shines
Golang Shines
Golang Shines
Write Elegant Go Handlers by Packing Boilerplate with a Generic Wrap

Identify the pain point

Typical handlers repeat five steps—decode, validate, convert types, call the service, and encode the response—resulting in a lot of boilerplate that looks like a photocopier. Each new endpoint repeats the same "pipeline code".

Solution: give the pipeline a uniform wrapper

Since every endpoint shares the same pipeline, we can write a generic Wrap function that bundles the repetitive work, allowing the handler to do only one thing: invoke business logic.

Step 1: Keep business functions pure

// Domain layer: pure business logic, no HTTP/gRPC dependencies
func (s *Service) Greet(ctx context.Context, in GreetIn) (GreetOut, error) {
    user, _ := s.users.Get(in.UserID) // fetch user
    msg := "hey " + user.Name + "!"   // build greeting
    return GreetOut{Message: msg}, nil
}

Step 2: Write a generic "universal wrapper"

// Simplified Wrap: packs pipeline logic into a function
func Wrap[In, Out any](
    decode func(*http.Request) (In, error),   // ① how to decode
    business func(context.Context, In) (Out, error), // ② core business
    encode func(http.ResponseWriter, Out) error,   // ③ how to encode
) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        in, _ := decode(r)                     // automatic decode
        out, _ := business(r.Context(), in)      // call business
        _ = encode(w, out)                       // automatic encode
    })
}

Step 3: Register routes in one line

// Before: ~30 lines of boilerplate per endpoint
// After: just three lines
mux.Handle("POST /greet", Wrap(
    decodeGreet,          // tell Wrap how to decode
    svc.Greet,            // core business (reused)
    encodeGreet,          // tell Wrap how to encode
))

Real benefits after refactoring

✅ Adding a new endpoint goes from writing 50 lines of boilerplate + 20 lines of business to only 10 lines of decode/encode + 20 lines of business.

✅ Changing validation logic requires a single change that instantly applies to all endpoints.

✅ Test layering: business logic can be unit‑tested without transport concerns; pipeline logic is tested once per transport.

The generic Wrap ensures compile‑time type safety: the type returned by decode must match the input of the business function, and the type returned by the business function must match the input of encode, eliminating runtime panics.

Avoiding pitfalls

Validate should be optional; use a type assertion like any(in).(validator) so only structs that implement Validate() run validation.

Centralise error mapping: translate domain errors (e.g., UserNotFound) to HTTP/gRPC status codes in one place instead of scattering guesses across handlers.

Don’t turn Wrap into a Swiss‑army‑knife that also handles routing, rate‑limiting, or circuit‑breaking—keep it single‑responsibility: the "how" (decode/encode) and the "what" (business) are separate.

This design follows the principle "Handler Thin, Service Fat": handlers contain only transport glue, while services hold all business logic.

Source: Huawei Cloud Community, https://bbs.huaweicloud.com/blogs/477727

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

goGenericstype safetyhandlerservice-layerthin-handlerwrap
Golang Shines
Written by

Golang Shines

We share daily the latest Golang technical articles, practical resources, language news, tutorials, and real-world projects to help everyone learn and improve.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.