Todo list server
The todo-list example is the canonical go-swagger server: a small CRUD API
generated from a spec, wired to a trivial in-memory store. It’s the best place to
see what swagger generate server produces and which files you’re expected to
edit.
Prefer a step-by-step build? Start with the todo-list tutorial. This page is the reference tour of the finished example.
Tip
Source: todo-list/.
Regenerate with go run ./hack/tools regen (or the //go:generate directive in
restapi/configure_todo_list.go).
The spec
Two definitions drive everything: an item (with a required, minLength: 1
description and a read-only id) and a generic error.
item:
type: object
required:
- description
properties:
id:
type: integer
format: int64
readOnly: true
description:
type: string
minLength: 1
completed:
type: boolean
error:
type: object
required:
- message
properties:
code:
type: integer
format: int64
message:
type: stringFull source: todo-list/swagger.yml
The readOnly: true on id means the server assigns it — clients that send one
have it ignored on create.
Generated models
Each definition becomes a Go struct. Note how the spec constraints map onto the
type: description is required and minLength: 1, so it’s generated as a
non-pointer-safe *string carrying validation, while the read-only id is a
plain int64.
type Item struct {
// completed
Completed bool `json:"completed,omitempty"`
// description
// Required: true
// Min Length: 1
Description *string `json:"description"`
// id
// Read Only: true
ID int64 `json:"id,omitempty"`
}Full source: todo-list/models/item.go
The generator also emits the validation methods that enforce the spec’s
constraints at runtime — here the description field’s required and
minLength: 1 rules, wired straight from the spec. You never hand-write this:
func (m *Item) validateDescription(formats strfmt.Registry) error {
if err := validate.Required("description", "body", m.Description); err != nil {
return err
}
if err := validate.MinLength("description", "body", *m.Description, 1); err != nil {
return err
}
return nil
}Full source: todo-list/models/item.go
Wiring the handlers
restapi/configure_todo_list.go is the one file you edit — it’s marked safe to
edit and survives regeneration. The generated scaffold leaves each handler
returning 501 Not Implemented; you replace those with real logic. Here’s the
implemented version from the tutorial’s server-complete, backing the store with
a map:
api.TodosAddOneHandler = todos.AddOneHandlerFunc(func(params todos.AddOneParams) middleware.Responder {
if err := addItem(params.Body); err != nil {
return todos.NewAddOneDefault(500).WithPayload(&models.Error{Code: 500, Message: conv.Pointer(err.Error())})
}
return todos.NewAddOneCreated().WithPayload(params.Body)
})
api.TodosDestroyOneHandler = todos.DestroyOneHandlerFunc(func(params todos.DestroyOneParams) middleware.Responder {
if err := deleteItem(params.ID); err != nil {
return todos.NewDestroyOneDefault(500).WithPayload(&models.Error{Code: 500, Message: conv.Pointer(err.Error())})
}
return todos.NewDestroyOneNoContent()
})Full source: tutorials/todo-list/server-complete/restapi/configure_todo_list.go
Each response constructor (NewAddOneCreated, NewDestroyOneNoContent, …) is
generated from a response you declared in the spec, so the compiler keeps your
handlers honest against the contract.
Running it
The generated server listens on a Unix socket, HTTP and HTTPS by default. For a quick local test, enable just the HTTP listener on a fixed port:
See the todo-list README for the full listener matrix (unix/http/https) and TLS options.
Variations
- Strict server — the strict responder interface.
- Custom error handling — shaping error responses.
- Todo list tutorial — build this from scratch.