Golden Sample Backend
The golden sample backend is the project Anvil uses to test real application shape. It is intentionally larger than a quickstart because it needs to expose the cases that only appear in company backends.
Repository:
github.com/TDB-Group/anvil-sample-backendWhat It Exercises
Section titled “What It Exercises”- Grouped
/api/v1routes - Controller route tables
- Read and write policies
- HTTP middleware with
HandleHTTP,BeforeHTTP,AfterHTTP, andOnHTTPError - GraphQL, gRPC, and queue middleware through protocol-specific SDK contexts
- Typed param, query, header, body, and local binding
- Custom domain ID parsing with
encoding.TextUnmarshaler - Declarative validation
- Request-level custom validation
- Named DI providers for read and write stores
- Bundles for controller service groups
- Global domain error mapping
- Global error event capture
- Recovered panic events
- Event bus subscriptions
- Queue job generation and dispatch
- Cron-style lifecycle plugin example
- HTTP streaming
- WebSocket upgrades
- Generated manifest and OpenAPI
- Compiler lint rules
- Testbed stress cases
Project Shape
Section titled “Project Shape”api/ route groups, controllers, middleware, DTOsinternal/project/ domain model, store, index, event loginternal/jobs/ queue contracts and queue jobsinternal/cron/ lifecycle plugin exampleinternal/config/ runtime configinternal/observability/ error mapper, sink, plugininternal/smoke/ generated-backend smoke testsanvilgen/ generated wiringThe sample is split this way so you can check whether Anvil fits n-tier, hexagonal, CQRS, or functional-CQRS projects. Anvil removes wiring work without forcing one application architecture.
Handler Pattern
Section titled “Handler Pattern”Handlers read like business flow. Repeated response metadata can live in local helpers instead of package-level SDK functions.
func (p *Projects) Create(ctx sdk.Ctx, req CreateProjectReq) (ProjectDTO, error) { created, err := p.Services.WriteStore.Create(ctx.Context(), req.input()) if err != nil { return ProjectDTO{}, err } if err := p.publishReindex(ctx, created.ID, req.Actor); err != nil { return ProjectDTO{}, ctx.Errors().Wrap(err, "publishing project reindex") }
p.publishProjectCreated(created) return createdProject(ctx, created), nil}That keeps the SDK small while leaving application handlers readable.
Middleware Pattern
Section titled “Middleware Pattern”The sample uses separate middleware types per protocol:
type HTTPRequestTrace struct{}type GraphQLTrace struct{}type RPCTrace struct{}type QueueTrace struct{}That is the recommended shape for application code. The concern is the same,
but each protocol has a different context and return value. HTTP middleware can
set response headers and locals. GraphQL middleware can shape
sdk.GraphQLResponse.Extensions. gRPC middleware can inspect
ctx.FullMethod() and ctx.StreamKind(). Queue middleware can inspect the
broker-neutral sdk.QueueMessage.
One type can implement several protocol methods when a shared library needs that, but the sample keeps them split so it is obvious which middleware runs for which endpoint.
Read the middleware files by protocol:
- HTTP middleware uses
BeforeHTTP,HandleHTTP,OnHTTPError, orAfterHTTP. It runs for REST routes and WebSocket upgrades. - GraphQL middleware uses
HandleGraphQL. It wrapsExecuteandSubscribeon GraphQL endpoints. - gRPC middleware uses
HandleGRPC. It wraps unary and streaming RPC methods. - Queue middleware uses
HandleQueue. It runs after the selected queue driver converts a broker message intosdk.QueueMessage.
ctx.Next() exists only inside middleware. It continues the generated chain.
Handlers return their normal protocol result. When a middleware returns before
calling ctx.Next(), the downstream handler is skipped for that request,
operation, RPC call, or queue delivery.
Use the sample as an audit checklist: find the route group, check which
sdk.Use[...] markers it inherits, then look at the protocol method on that
middleware type. That tells you exactly which code runs before the handler.
Verification Commands
Section titled “Verification Commands”From the sample backend:
go test -count=1 ./...go vet ./...Run Anvil tooling against the sample as well:
anvil lint .anvil manifest .anvil openapi .anvil testbed . --stress 6 --seed 42