Start with one clear endpoint
A small service does not need much scaffolding. The net/http package gives you a request, a response writer, and a router. Begin with a health endpoint so that you can test the whole path from a browser to your process.
package main
import (
"encoding/json"
"log"
"net/http"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(map[string]string{
"status": "ok",
}); err != nil {
log.Print(err)
}
})
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Fatal(server.ListenAndServe())
}Run the complete example
Save the file as main.go in a new directory. Initialize a module, start the process, and ask for the health endpoint. The method-aware routing pattern in this example requires Go 1.22 or newer.
go mod init example.com/tiny-server
go run .
curl -i http://localhost:8080/healthChoose timeouts for the actual workload
ReadHeaderTimeout bounds the time spent reading request headers. WriteTimeout bounds writes for ordinary request handling, and IdleTimeout controls idle keep-alive connections. These values are a starting point for a small JSON service; a streaming response or a large file upload needs different limits.
Decide when to send an error
Set response headers before writing the body. A successful body write implicitly sends status 200, so an error discovered afterward cannot reliably become a 500 response. For more complicated JSON handlers, encode into a buffer first, then set headers and write the result. Add graceful shutdown when the service needs to finish requests during deployment.
net/http package reference ↗