Building a microservice with servo
A step-by-step, from-scratch build of a real order-management service — HTTP API, JWT auth, Postgres, Redis, NATS, Prometheus metrics, OpenTelemetry tracing, a circuit breaker, a full test suite, CI/CD, and a Docker deployment — using servo to wire and run all of it.
Every chapter’s code lives in examples/tutorial, a real, separate Go
module you can cd into and run at every step. Nothing in these pages is invented: every code
block is copied from that module, and every command’s output shown here was actually run.
Why this exists
servo’s own README and examples/basic show the
mechanics — how one constructor gets wired to another, what a spec file looks like, what
servo generate produces. What they don’t show is what it’s like to build something with real
weight to it: a service with a database, a cache, an event to publish, a login flow, tests at
three different levels, and a pipeline that ships it. That gap — “I understand what servo does” to
“I could build a real service with it” — is what this tutorial is for. Every chapter adds one
layer or concern to the same running service, in the order you’d actually add them, so by the last
chapter nothing is hypothetical: it’s a service you’ve built, one working piece at a time, that
happens to also be a complete reference for how the pieces fit together.
How to read this
Each chapter follows the same shape: what problem this layer solves and why it exists, the real code that solves it (with enough surrounding explanation that you’re not just staring at syntax), a diagram where the shape of something is easier to see than to describe, a “try it yourself” section with commands you can actually run, then diagnostics, do’s and don’ts, and pointers to alternatives. Read chapters in order the first time — each one builds on code the previous ones already wrote. After that, they hold up fine as standalone reference.
Who this is for
You should be comfortable writing Go and using go test, but this tutorial assumes nothing about
prior exposure to dependency injection, microservice architecture, or any of the specific
libraries used (Postgres, Redis, NATS, JWT, Prometheus, OpenTelemetry). Each is introduced when
it’s first needed, with a short “what and why” before the code.
Prerequisites
- Go 1.27 or newer (
go version) - Docker with the
docker composeplugin, for running Postgres, Redis, and NATS locally curl(or any HTTP client) for the “try it yourself” sections- No cloud account of any kind is required — everything runs on your machine
What you’ll build
flowchart TB
Client(["HTTP client"])
subgraph api["API layer"]
Router["Router + middleware<br/>(auth, logging, recovery, rate limit)"]
end
subgraph svc["Service layer"]
OrderService
end
subgraph data["Data"]
PG[("Postgres")]
Redis[("Redis")]
end
subgraph msg["Messaging"]
NATS[("NATS")]
Notifier["notifier<br/>(subscriber)"]
end
Client -->|"Bearer JWT"| Router
Router --> OrderService
OrderService -->|"read/write orders"| PG
OrderService -->|"cache-aside reads"| Redis
OrderService -->|"publish OrderPlaced"| NATS
NATS -->|"consume"| Notifier
A user logs in, gets a JWT, and can place and view orders. Placing an order writes to Postgres,
invalidates the cache, and publishes an OrderPlaced event; a separate notifier component
consumes that event to show the “other side” of an event-driven system without needing an actual
second service. Every arrow in that diagram is a real dependency servo resolves and wires for
you — nothing here is assembled by hand.
Chapters
| # | Chapter | What it covers |
|---|---|---|
| 1 | Architecture overview | Layers, why layered, where servo fits |
| 2 | Project setup | Module layout, tools, the Makefile |
| 3 | Configuration | Typed env config, validation, secrets |
| 4 | Domain layer | Core types, framework-free |
| 5 | Repository layer | Postgres, migrations, connection pooling |
| 6 | Caching layer | Redis, cache-aside, invalidation |
| 7 | Messaging layer | NATS, publish/subscribe, delivery guarantees |
| 8 | Service layer | Business logic, orchestration, domain errors |
| 9 | Authentication | JWT issue/verify, password hashing, middleware |
| 10 | API layer | Routing, DTOs, validation, error mapping |
| 11 | Gin as the transport | The same API in Gin, and what the swap costs |
| 12 | gRPC as the transport | The same API over gRPC, on one port with REST |
| 13 | Wiring with servo | The spec file, capabilities, servo generate |
| 14 | Scoped instances | One session per user, instead of one per process |
| 15 | Observability | Structured logs, metrics, tracing, health checks |
| 16 | Resilience | Circuit breaker, rate limiting, graceful shutdown |
| 17 | Testing strategy | Unit, integration, and API-level tests |
| 18 | CI/CD | GitHub Actions: lint, test, build, servo check |
| 19 | Running and deployment | Docker Compose, Dockerfile, env reference |
| 20 | Troubleshooting | Every diagnostic from every chapter, organized by symptom |
| 21 | Alternatives and further reading | Other valid choices at every layer |
Read them in order the first time through — each one assumes the code from the previous chapters already exists. After that, they stand alone well enough to use as reference.