Building a Production REST API in Go

A twenty-part guide to building a Go REST service end to end: configuration, lifecycle, servers, handlers, database, observability, testing, packaging and deployment.


Most guides to writing an HTTP service in Go stop at the handler. It is the part that is easiest to teach, and the smallest part of what it takes to run the service.

Production is not the environment the code was written in. The process does not run once on a laptop under a developer’s eye: it is started, restarted, scaled to many copies, rescheduled onto another node and killed mid-request by an orchestrator that knows nothing about what it was doing. It runs against configuration nobody re-read, on dependencies that are up but slow, under load that arrives in bursts, from clients that retry. Its failures are rarely a clean crash and usually a degradation: a few instances answering slowly, a connection pool exhausted since the last deploy, a timeout budget larger than the caller’s patience. Nobody is attached to the process, so what you learn about it afterwards is only what it wrote down while it was still working.

That is why the work that keeps a service alive sits around the handler rather than inside it: configuration that fails at startup instead of being wrong for six hours before anyone notices, a shutdown that drains in-flight requests before the socket closes, a readiness probe that answers no until the database is actually reachable, a retry policy that does not turn a slow upstream into a dead one, and signals detailed enough to be worth reading at three in the morning.

This guide is organised around the service’s runtime rather than around a feature list. What happens before the first request arrives, what happens during one, and what happens after the last one. Twenty pages, in six parts, building one real service.

Who this is for

You can write Go and you know what a 404 is. Nothing else is assumed.

Nothing Go-specific about serving HTTP is taken as given. net/http and the types it gives you, routing, middleware, the request context, the goroutine-per-request model, connection pools, graceful shutdown, health endpoints and the delivery machinery around them are explained as the service needs them, in the order it needs them. Read front to back and you get one service assembled from main outwards.

The pages also stand on their own. Each one opens with the problem before any code, so if you have shipped services before you can go straight to the decisions you have not had to make yet, or to the ones you made differently and want to argue with. The parts that most often go unconsidered are Part IV and Part VI.

The service

The running example is an inventory service, and it is the examples/service reference implementation from nurago, a collection of Go packages for the parts of a backend service that are not the business logic.

The examples/ path names a template rather than a demo: it ships the Makefile, CI workflows, OpenAPI specs, Docker files, the configuration layout a service needs on day one, and a small working feature, four endpoints over an item table. make project renames it into your own project, and this guide’s copy is called inventorysvc.

Every Go snippet in this guide is code from that tree, at the path named above it. Where the guide goes past what the example ships, and Part V does that four times, the section says so in its first line.

The toolkit is a means and not the subject. Every page starts from a problem the service has, and reaches for a package only once the problem is on the table. The standard library’s own behaviour is stated before anything is wrapped around it, so you can see the boundary and decide what to keep. Where a reasonable engineer would pick something else, that alternative is named.

Part I. Orientation

The mental model the rest of the guide assumes.

  1. What a REST Service Is Made Of
  2. The Standard Library Underneath All of This
  3. From main to Listening

Part II. Foundations

The three things every request depends on and none of them touch directly.

  1. Configuration That Is Wrong Before It Is Late
  2. The Lifecycle: One Context, One Channel, One Wait Group
  3. Logs You Can Search, and Secrets You Cannot Read

Part III. Serving

From a bound socket to a response body.

  1. Servers, Binders, and the Routes You Get for Free
  2. The Middleware You Will Actually Need
  3. One Request, All the Way Down
  4. The Envelope Argument

Part IV. Dependencies

Everything the service cannot do by itself.

  1. Talking to Other Services Without Losing the Thread
  2. The Database Connection Is a Long-Lived Thing
  3. Ping, Status, Metrics: Three Different Questions

Part V. A Real Feature, End to End

The item resource the example ships, from an OpenAPI operation to a row and back, and what it deliberately leaves out.

  1. Designing the Feature: Contract, Schema, Seams
  2. The Write Path: Decode, Validate, Commit
  3. The Read Path: What the Client Can Ask For
  4. What Is Reachable From Where

Part VI. Delivery

Getting it into production and keeping it there.

  1. Tests That Would Have Caught It
  2. Building, Packaging, and the Probes That Watch It
  3. Growing the Service Without Growing the Wiring

Going deeper on a single package

The guide covers the options a service actually sets. Several of the packages it uses have their own article here, going further into the engineering than a guide page has room for:

The package reference and the comparison against other libraries live on nurago.org.

Start at What a REST Service Is Made Of.