19 — Web Development — net/http, Frameworks, and gRPC
The domain Go was built for, and the surprise: the standard library is already most of the server. The model that clicked: Go's standard net/http is already a complete, production-grade HTTP server with HTTP/2 and TLS built in, so Go web frameworks are thin convenience layers that add routing and middleware ergonomics on top — not foundational dependencies the way they are in other ecosystems. [1] The choice between bare net/http, a minimal framework (Gin, Echo, Fiber), and gRPC is about how much structure a service needs, not whether it can be built at all.
net/http — the complete standard server
The standard library ships an HTTP server that handles routing, TLS, HTTP/2, cookies, and streaming out of the box [1][2]:
http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
json.NewEncoder(w).Encode(users)
case http.MethodPost:
var u User
json.NewDecoder(r.Body).Decode(&u)
users = append(users, u)
w.WriteHeader(http.StatusCreated)
}
})
log.Fatal(http.ListenAndServe(":8080", nil))A handler is any function with the signature func(http.ResponseWriter, *http.Request). The standard mux (http.ServeMux) does simple prefix-based routing. For a small service, this is genuinely enough — many production Go APIs run on the standard library with no framework at all. The historical complaint (no path parameters, no method-based routing in the standard mux) is addressed by the enhanced routing in Go 1.22's ServeMux, which now supports method and path patterns like "GET /users/{id}".
Gin — the popular productivity choice
Gin is the most popular Go web framework, emphasizing performance and developer ergonomics [3]. It adds on top of net/http:
- Path parameters — c.Param("id") from a route like /users/:id.
- Middleware — composable functions wrapping handlers for logging, auth, recovery.
- JSON binding and validation — c.ShouldBindJSON(&u) decodes and validates the request body in one call.
- Error management — a built-in way to collect errors from middleware and handlers.
r := gin.Default()
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
c.JSON(200, gin.H{"id": id})
})
r.Run(":8080")Gin uses httprouter-style routing — a tree-structured lookup that is very fast. Its API reads cleanly and the documentation is thorough, making it the "pick this one" default for teams that want framework ergonomics without overcommitting.
Echo and Fiber — minimalist alternatives
Echo is a high-performance, minimalist framework with a focus on ease and speed [4]. Its pitch is clean API, automatic TLS via Let's Encrypt, HTTP/2, and built-in middleware for CORS, JWT, logging, and compression. It sits at roughly the same complexity as Gin; the choice between Gin and Echo is often organizational familiarity.
Fiber is an Express-inspired framework built on fasthttp rather than net/http [5]. fasthttp is a custom HTTP implementation tuned for raw throughput, so Fiber benchmarks faster than Gin/Echo — but the tradeoff is that it does not use the standard net/http interfaces, so some middleware and libraries that target http.Handler are incompatible. Fiber is the choice when benchmarked throughput is the deciding factor and the ecosystem incompatibility is acceptable.
The key fact for all three: they are thin layers. A handler is still fundamentally "read the request, do work, write the response," and the standard library's http.Handler interface is the common currency across most of them (Fiber excepted).
Beego — the full-stack option
Beego is a full-stack framework in the Rails/Django sense: it bundles an ORM, session management, caching, logging, MVC scaffolding, and an admin interface generator [6]. It follows convention over configuration. The tradeoff is the same as in every language: a full-stack framework gets you to "working CRUD app" fast but imposes its conventions on everything. Beego is the right call when the team wants everything included up front and is building a monolith; for a focused API or microservice, it carries weight that Gin/Echo do not.
gRPC and Protocol Buffers — the typed microservice case
For service-to-service communication where type safety and performance matter more than browser compatibility, gRPC is the standard [7]. It uses Protocol Buffers (protobuf) as the serialization format — a compact binary schema — and generates client and server stubs from a .proto definition file:
service UserService {
rpc GetUser(GetUserRequest) returns (User);
}
message GetUserRequest { int64 id = 1; }
message User { int64 id = 1; string name = 2; }From this, the protobuf compiler generates a Go client interface and a server interface; you implement the server methods, and callers get a typed client with no manual marshaling. gRPC supports bidirectional streaming (both client and server can stream), authentication, load balancing, and is cross-language — a Go service can talk to a Python or Java service with the same .proto. The cost is that it is not browser-native (gRPC uses HTTP/2 with a binary body), so public-facing APIs still use REST/JSON, and gRPC sits behind the public boundary, between internal services.
How I use this
The decision tree is now straightforward. For a small service or one where I want zero dependencies, the standard net/http (especially with the Go 1.22 router) is genuinely enough — "the standard library is the framework." For an API with many routes, middleware, and JSON binding, Gin is my default; it adds the ergonomics without imposing a worldview. gRPC enters the picture the moment two internal services need to talk with typed contracts and possibly streaming, because the generated stubs and cross-language story beat hand-written JSON clients. The thing I do _not_ do is reach for a framework reflexively — the question is always "what does net/http not give me that I actually need?" and often the answer is "nothing important."
References
[1] The Go Authors, "net/http package," pkg.go.dev, 2024. [Online]. Available: https://pkg.go.dev/net/http
[2] E. Hasan, "net/http package in Go," Medium, 2024. [Online]. Available: https://medium.com/@emonemrulhasan35/net-http-package-in-go-e178c67d87f1
[3] Gin-Gonic, "Gin Web Framework," gin-gonic.com, 2024. [Online]. Available: https://gin-gonic.com/
[4] Labstack, "Echo — High Performance, Extensible, Minimalist Go Web framework," echo.labstack.com, 2024. [Online]. Available: https://echo.labstack.com/
[5] GoFiber, "Fiber," gofiber.io, 2024. [Online]. Available: https://gofiber.io/
[6] Beego, "beego package," pkg.go.dev, 2024. [Online]. Available: https://pkg.go.dev/github.com/beego/beego
[7] The Go Authors, "google.golang.org/grpc package," pkg.go.dev, 2024. [Online]. Available: https://pkg.go.dev/google.golang.org/grpc
Knowledge check · Question 1 of 5
Why are Go web frameworks described as "thin layers"?
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!