A curated collection of runnable examples for
go-swagger β generating servers,
clients and CLIs from an OpenAPI 2.0 (Swagger) spec.
Every example here is committed to the
go-swagger/examples repository and kept
in sync with the latest go-swagger release by automated regeneration.
Status
Fork me
Actively maintained. Regenerated weekly against swagger@master.
Which site do I want?
These examples are all spec-first: you have an OpenAPI spec and want
swagger generate to produce typed code. The sibling sites cover the other two
approaches β pick by what you start from:
Install the toolchain and read the command reference on go-swagger’s own site:
go install github.com/go-swagger/go-swagger/cmd/swagger@latest
β go-swagger.io for install, the generate
command families, and project-layout reference. This site assumes you have
swagger on your PATH and focuses on what to build with it.
Where to go next
Guides
The example catalog, grouped by concern β servers, clients & CLI, authentication,
streaming, and codegen customization. One page per example.
The example catalog, grouped by concern. Each page covers one runnable example:
what it demonstrates, the spec excerpt, the generate command, how to run it, and
the key generated files to look at.
Sequential, end-to-end walkthroughs. Unlike the guides β which are reference
recipes you dip into β these build something from scratch, step by step. Start
with the todo-list to generate both a server and a client.
Generating HTTP servers from a spec β from the canonical todo-list server to
strict handlers, custom error handling, file upload/download and CRUD APIs.
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: 1description and a read-only id) and a generic error.
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.
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:
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:
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:
$ go run ./todo-list/cmd/todo-list-server --scheme=http --port=8765
serving todo list at http://127.0.0.1:8765
The strict server is generated with --strict-responders. Instead of every
handler returning a generic middleware.Responder β which lets you hand back
any response, including ones the spec never declared β each operation gets its
own responder interface. The compiler then enforces that a handler can only
return responses declared for that operation.
Tip
Source: todo-list-strict/.
Generated with swagger generate server -A todo-list -f ./swagger.yml --strict-responders --regenerate-configureapi.
The generated responder interface
For each operation, the generator emits a marker interface embedding
middleware.Responder. Only the response types declared for addOne implement
AddOneResponder, so nothing else can be returned:
Every generated response for the operation β AddOneCreated, AddOneDefault,
AddOneNotImplemented β carries a no-op AddOneResponder() method, which is
what admits it to the interface. A FindDefault value, for instance, simply
won’t compile inside an addOne handler.
The handler signature
Compare with the default todo-list server, where the handler
returns middleware.Responder. Here the return type is the operation-specific
todos.AddOneResponder:
The scaffold guards each assignment with if β¦ == nil, so you can wire a handler
from elsewhere and leave the rest returning NotImplemented. You replace the
body with real logic, returning one of the operation’s typed responders.
When to use it
Reach for strict responders when you want the type system to guarantee your
handlers stay in sync with the contract β you can’t accidentally return a
response shape the spec doesn’t describe. The cost is a little more generated
surface (one interface per operation) and slightly more verbose returns.
By default a generated handler returns a middleware.Responder and you build
error responses yourself (NewAddOneDefault(500).WithPayload(...)). The
todo-list-errors example shows the alternative: generate with
--return-errors so handlers may return a plain error, and install a single
custom error handler that shapes every error response in one place.
Tip
Source: todo-list-errors/.
Generated with swagger generate server -A TodoList -f ./swagger.yml --return-errors
(the //go:generate line in configure_todo_list.go uses the long-form flags).
Handlers that return an error
With --return-errors, the handler signature becomes
func(params) (middleware.Responder, error). A handler can now short-circuit by
returning an error instead of constructing a response:
Here errAlreadyExists is a sentinel the handler returns directly β no response
plumbing at the call site.
The centralized error handler
Returned errors flow through api.ServeError, which you can override. This
example wires it to a catcher that recognizes the sentinel (with
errors.Is), logs it, then defers to the runtime’s default ServeError for the
actual HTTP response:
Because every operation’s error funnels through the same hook, you get one place
to classify errors, attach correlation IDs, translate domain errors to status
codes, or emit metrics β without repeating that logic in each handler.
When to use it
Use returned errors when your handlers naturally surface Go error values (a
data layer, a validation step) and you’d rather map them to responses centrally
than build a *Default responder at every return. Stick with the default
responder style when each handler already knows the exact response it wants.
Hand-wiring error handling without codegen? See the
go-openapi/runtime examples.
Auto-configure
Normally you edit configure_*.go by hand to attach each handler. The
auto-configure example takes a different route: generate with
--implementation-package, point it at a package you write, and the generator
emits the wiring for you. Every operation is routed to a method on your
implementation β no hand-editing of a configure file.
Tip
Source: auto-configure/.
Generated with swagger generate server --name AToDoListApplication --spec ./swagger.yml --implementation-package github.com/go-swagger/examples/auto-configure/implementation --principal any.
The generated contract
Instead of a configure_*.go, the generator produces
auto_configure_*.go. It declares the Handler interface your package must
satisfy, and binds a package-level Impl to your constructor via
implementation.New():
varImplHandler = implementation.New()
// Handler handles all api server backend configurations and requeststypeHandlerinterface {
AuthableConfigurableTodosHandler}
// Configurable handles all server configurationstypeConfigurableinterface {
ConfigureFlags(api*operations.AToDoListApplicationAPI)
ConfigureTLS(tlsConfig*tls.Config)
ConfigureServer(s*http.Server, scheme, addrstring)
CustomConfigure(api*operations.AToDoListApplicationAPI)
SetupMiddlewares(handlerhttp.Handler) http.HandlerSetupGlobalMiddleware(handlerhttp.Handler) http.Handler}
// Authable handles server authenticationtypeAuthableinterface {
// Applies when the "x-todolist-token" header is setKeyAuth(tokenstring) (any, error)
}
// TodosHandlertypeTodosHandlerinterface {
AddOne(paramstodos.AddOneParams, principalany) middleware.ResponderDestroyOne(paramstodos.DestroyOneParams, principalany) middleware.ResponderFindTodos(paramstodos.FindTodosParams, principalany) middleware.ResponderUpdateOne(paramstodos.UpdateOneParams, principalany) middleware.Responder}
You write a package that implements Handler. This is ordinary hand-written code
β nothing generated β so it’s the natural home for your business logic. Here’s
the AddOne method backing the in-memory store:
The example splits the interface across small types β TodosHandlerImpl,
ConfigureImpl, AuthImpl β composed by a single HandlerImpl that New()
returns. That keeps handlers, server configuration, and authentication in
separate files while still satisfying the one generated Handler interface.
When to use it
Auto-configure shines when you regenerate often and don’t want a hand-edited
configure_*.go in the loop: your implementation lives entirely in a package you
own, and regeneration only ever touches the generated wiring. It’s also a clean
way to keep the transport-facing glue separate from your domain code.
Related
Todo list server β the conventional hand-wired configure_*.go.
The file-server example demonstrates a file-upload endpoint: how the spec’s
type: file maps onto the generated server and client, and how the runtime
surfaces the uploaded file to your handler.
Tip
Source: file-server/.
Build the server under restapi/cmd/file-upload-server, then run the client with
go run upload_file.go swagger.yml.
The spec
An upload is a multipart/form-data operation with a formData parameter of
type: file:
The generated handler receives the file as an io.ReadCloser on
params.File. At runtime it’s a *runtime.File, so a type assertion gives you
the multipart header β filename and size β before you stream the body to disk:
api.UploadsUploadFileHandler = uploads.UploadFileHandlerFunc(func(paramsuploads.UploadFileParams) middleware.Responder {
ifparams.File==nil {
returnmiddleware.Error(http.StatusNotFound, stderrors.New("no file provided"))
}
deferfunc() {
_ = params.File.Close()
}()
ifnamedFile, ok:=params.File.(*runtime.File); ok {
log.Printf("received file name: %s", namedFile.Header.Filename)
log.Printf("received file size: %d", namedFile.Header.Size)
}
// uploads file and save it locallyfilename:=path.Join(uploadFolder, fmt.Sprintf("uploaded_file_%d.dat", uploadCounter))
uploadCounter++f, err:=os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
iferr!=nil {
returnmiddleware.Error(http.StatusInternalServerError, stderrors.New("could not create file on server"))
}
n, err:=io.Copy(f, params.File)
iferr!=nil {
returnmiddleware.Error(http.StatusInternalServerError, stderrors.New("could not upload file on server"))
}
log.Printf("copied bytes %d", n)
log.Printf("file uploaded copied as %s", filename)
returnuploads.NewUploadFileOK()
})
Note the defer params.File.Close() and the io.Copy into a fresh file β the
handler owns the stream and is responsible for draining and closing it.
Client side
On the client, a file argument is a runtime.NamedReadCloser β an
io.ReadCloser that also reports a Name(). A plain *os.File satisfies it, so
you open the file and pass it straight to the generated parameter builder:
The generated UploadFile client method handles the multipart encoding; you only
supply the reader.
Related
Streaming request/response bodies instead of a one-shot upload? See
Streaming.
Hand-wiring multipart without codegen? See the
go-openapi/runtime examples.
Task tracker
The task-tracker example is a larger, realistic CRUD API β an issue tracker
with tasks, comments and file attachments. Its spec is the one go-swagger uses to
exercise code generation, so it deliberately packs in almost every construct:
nested resources, composition, arrays, file uploads, and multiple security
schemes. It’s the best example to see what the generator produces for a
non-trivial contract.
Tip
Source: task-tracker/.
Generated with swagger generate server --name TaskTracker --spec ./swagger.yml --principal any.
The API surface
The spec defines a full CRUD surface across three nested resources:
Path
Operations
/tasks
listTasks, createTask
/tasks/{id}
getTaskDetails, updateTask, deleteTask
/tasks/{id}/comments
addCommentToTask, getTaskComments
/tasks/{id}/files
uploadTaskFile
Each becomes a typed handler on the generated API, with parameters (path, query,
body, multipart) bound and validated for you.
Model composition
The spec builds Task on top of a shared TaskCard, and the generator preserves
that with Go embedding β plus read-only fields, maps of attachments, and slices of
related models:
typeTaskstruct {
TaskCard// The attached files.//
// An issue can have at most 20 files attached to it.//
Attachments map[string]TaskAttachmentsAnon `json:"attachments,omitempty"`// The 5 most recent items for this issue.//
// The detail view of an issue includes the 5 most recent comments.// This field is read only, comments are added through a separate process.//
// Read Only: trueComments []*Comment`json:"comments"`// The time at which this issue was last updated.//
// This field is read only so it's only sent as part of the response.//
// Read Only: true// Format: date-timeLastUpdatedstrfmt.DateTime`json:"lastUpdated,omitempty"`// last updated byLastUpdatedBy*UserCard`json:"lastUpdatedBy,omitempty"`// reported byReportedBy*UserCard`json:"reportedBy,omitempty"`}
The generator turns each into an authenticator hook you implement. The scaffold
leaves them returning NotImplemented; you fill in the token validation:
// Applies when the "token" query is setifapi.APIKeyAuth==nil {
api.APIKeyAuth = func(tokenstring) (any, error) {
_ = tokenreturnnil, errors.NotImplemented("api key auth (api_key) token from query param [token] has not yet been implemented")
}
}
// Applies when the "X-Token" header is setifapi.TokenHeaderAuth==nil {
api.TokenHeaderAuth = func(tokenstring) (any, error) {
_ = tokenreturnnil, errors.NotImplemented("api key auth (token_header) X-Token from header param [X-Token] has not yet been implemented")
}
}
File server β the type: file upload mechanics used by /tasks/{id}/files.
Petstore β another full spec, generated end to end.
Petstore
The Swagger Petstore is the canonical OpenAPI 2.0 sample. The generated/
example is that spec run through swagger generate server β a complete server
scaffold for a multi-resource API, useful as a reference for what a “full” spec
produces.
Tip
Source: generated/.
Generated with swagger generate server --name Petstore --spec ./swagger-petstore.json --principal any.
Three resource groups
The petstore spec organizes operations under three tags, each becoming its own
package of generated handlers:
pet β addPet, updatePet, findPetsByStatus, findPetsByTags,
getPetById, deletePet, uploadFile
store β getInventory, placeOrder, getOrderById, deleteOrder
user β createUser, getUserByName, loginUser, logoutUser, β¦
A generated model
The Pet model shows the usual spec-to-Go mapping β required fields as pointers,
nested models by reference, arrays as slices β alongside a generated Validate
method (not shown) that enforces the spec’s constraints:
typePetstruct {
// categoryCategory*Category`json:"category,omitempty"`// idIDint64`json:"id,omitempty"`// name// Example: doggie// Required: trueName*string`json:"name"`// photo urls// Required: truePhotoUrls []string`json:"photoUrls"`// pet status in the storeStatusstring`json:"status,omitempty"`// tagsTags []*Tag`json:"tags"`}
Petstore mixes an api-key header with an OAuth2 flow. The generator emits a
distinct authenticator hook for each β note the OAuth2 hook receives the required
scopes so you can authorize per operation:
// Applies when the "api_key" header is setifapi.APIKeyAuth==nil {
api.APIKeyAuth = func(tokenstring) (any, error) {
_ = tokenreturnnil, errors.NotImplemented("api key auth (api_key) api_key from header param [api_key] has not yet been implemented")
}
}
ifapi.PetstoreAuthAuth==nil {
api.PetstoreAuthAuth = func(tokenstring, scopes []string) (any, error) {
_ = token_ = scopesreturnnil, errors.NotImplemented("oauth2 bearer auth (petstore_auth) has not yet been implemented")
}
}
See the OAuth2 guide for a fully
implemented flow.
Typed vs untyped
This repo also ships the same petstore API hand-wired without codegen, using
the go-openapi runtime directly, under
2.0/petstore.
That untyped style β building the API from runtime primitives rather than
generated code β is the subject of the
go-openapi/runtime site. Compare the two
to see exactly what swagger generate buys you.
From the same spec that drives a server, swagger generate client produces a
typed Go SDK: one method per operation, with generated parameter and response
types. The tutorials/client example generates that SDK in two flavors from
one spec β the classic go-swagger client and the stratoscale contributed
template β so you can compare the ergonomics.
Tip
Source: tutorials/client/.
Two client packages are generated from one swagger.yml:
stratoscale β the same, plus --template stratoscale (reusing --existing-models).
Classic client
The classic client generates a ClientService interface. Each method takes the
operation’s params plus an explicit runtime.ClientAuthInfoWriter and variadic
ClientOptions; a parallel β¦Context variant threads a context.Context:
The stratoscale template generates a leaner API interface: context-first,
auth folded into the transport, no options parameter. It also emits a
//go:generate mockery directive so the interface is trivially mockable in tests:
// AddOne add one APIAddOne(ctxcontext.Context, params*AddOneParams) (*AddOneCreated, *AddOneNoContent, error)
Pick classic for the full go-swagger surface (per-call auth, per-call options);
pick stratoscale for a compact, context-first, easily-mocked client.
Multiple success responses
This example deliberately exercises a tricky spec shape: addOne declares two
success responses (201 Created and 204 No Content). Both flavors reflect that
in the return signature β the method hands back a pointer for each possible
success, and exactly one is non-nil:
created, noContent, err:=c.AddOne(ctx, params)
switch {
caseerr!=nil:
// transport or error responsecasecreated!=nil:
// 201 β use created.PayloadcasenoContent!=nil:
// 204 β nothing to read}
The same mechanism covers operations with no default response: without a
default, an undeclared status code surfaces as a generic error rather than a
typed payload, because the generated response reader only knows the codes the spec
listed.
Related
CLI client β a cobra command-line tool wrapping a generated client.
Hand-wiring a client without codegen? See the
go-openapi/runtime examples.
CLI client
swagger generate cli produces a command-line tool that wraps the generated
client: it reads flags and arguments, builds the operation parameters, calls the
server, and prints the response. It’s built on cobra
and viper, with shell-completion support.
Tip
Source: cli/.
Generated with swagger generate cli --spec ./swagger.yml --cli-app-name todoctl.
It targets the same spec as auto-configure, so you
can run that server and drive it from this CLI.
Command layout
The generated command tree mirrors the spec:
the root command holds global flags (--hostname, --scheme, auth tokens);
each tag becomes a sub-command (an operation group);
each operationId becomes a sub-command under its tag;
each path/query parameter becomes a flag; the body becomes a --body JSON flag,
with a flag per body field layered on top.
The tag β sub-command mapping is a cobra.Command per group, wiring in one child
per operation:
Body parameters are handled two ways at once β a whole-body --body JSON string
as a base payload, and a generated flag per field (recursing into sub-definitions)
that overrides it. That’s where --item.description comes from:
$ go run ./cli/cmd/todoctl/main.go --hostname localhost:12345 \
--x-todolist-token "example token" \
todos addOne --item.description "hi" --body "{}"
{"description":"hi"}
The path is todoctl β todos (the tag) β addOne (the operationId), with
--item.description setting a body field.
Config files and completion
Common flags β hostname, scheme, base_path, auth tokens β can live in a
config file instead of the command line, loaded via viper from
~/.config/<app>/config.json (or --config, in JSON/YAML/env form):
dockerctl β a full CLI generated this way for the Docker Engine API.
Authentication
Info
These examples wire authentication into generated servers. Looking to
hand-wire auth on an untyped runtime server instead? See the
runtime auth examples.
This is the starting point for security in a generated server. A
securityDefinition in the spec becomes a generated authenticator hook you
implement, and β when you generate with a typed principal β every protected
handler receives that principal as a typed argument.
Tip
Source: authentication/.
Generated with swagger generate server --name AuthSample --spec ./swagger.yml --principal models.Principal.
Declaring the scheme
The authentication example uses a single API-key scheme, carried in the
x-token header, and applies it to every endpoint via a top-level security
requirement:
Because the spec names one apiKey scheme called key, the generated API exposes
an api.KeyAuth hook. The --principal models.Principal flag makes it return a
typed *models.Principal; the scaffold leaves it returning NotImplemented:
ifapi.KeyAuth==nil {
api.KeyAuth = func(tokenstring) (*models.Principal, error) {
_ = tokenreturnnil, errors.NotImplemented("api key auth (key) x-token from header param [x-token] has not yet been implemented")
}
}
The returned principal is then passed to every handler protected by this scheme:
api.CustomersGetIDHandler = customers.GetIDHandlerFunc(
func(paramscustomers.GetIDParams, principal*models.Principal) middleware.Responder {
// principal is the value your KeyAuth hook returned... })
Basic auth is the same shape
A type: basic scheme works identically, except the runtime decodes the
Authorization: Basic header for you and the hook receives a username/password
pair instead of a single token:
Real APIs rarely have a single security scheme. The composed-auth example
mixes four β basic auth, an API key (by header or query), and scoped JWT
tokens β and composes them per operation with AND and OR semantics. It’s
the reference for anything beyond a single authenticator.
Tip
Source: composed-auth/.
Generated with swagger generate server --name multi-auth-example --spec ./swagger.yml --principal models.Principal.
The restapi/configure_*.go and auth/authorizers.go files are hand-written.
Four schemes
The spec declares basic auth (isRegistered), an API key in either the header
(isReseller) or a query param (isResellerQuery), and a scoped oauth2-typed
scheme (hasRole) used purely to carry JWT scopes β go-swagger does not run an
OAuth2 flow here, it just extracts the required scopes and hands them to your
authorizer:
isRegistered:
# This scheme uses the header: "Authorization: Basic {base64 encoded string defined by username:password}"# Scopes are not supported with this type of authorization.type: basicisReseller:
# This scheme uses the header: "X-Custom-Key: {base64 encoded string}"# Scopes are not supported with this type of authorization.type: apiKeyin: headername: X-Custom-KeyisResellerQuery:
# This scheme uses the query parameter "CustomKeyAsQuery"# Scopes are not supported with this type of authorization.type: apiKeyin: queryname: CustomKeyAsQueryhasRole:
# This scheme uses the header: "Authorization: Bearer {base64 encoded string representing a JWT}"# Alternatively, the query param: "access_token" may be used.## In our scenario, we must use the query param version in order to avoid# passing several headers with key 'Authorization'type: oauth2# The flow and URLs in spec are for documentary purpose: go-swagger does not implement OAuth workflowsflow: accessCodeauthorizationUrl: 'https://dummy.oauth.net/auth'tokenUrl: 'https://dumy.oauth.net/token'# Required scopes are passed by the runtime to the authorizerscopes:
customer: scope of registered customersinventoryManager: scope of resellers acting as inventory managers
A security block on an operation is a list of alternatives (OR), and each
alternative is a map of schemes that must all pass (AND). So /order/add
accepts a registered customer, or a reseller (by header), or a reseller (by
query) β each combined with the right JWT role:
An empty security: [] on an operation opts it out entirely (public), overriding
the spec’s top-level default.
The authorizers
Each scheme maps to a hook whose signature depends on its type: basic auth gets
(user, pass), an API key gets (token), and a scoped scheme gets
(token, scopes). The example delegates each to a function in
auth/authorizers.go:
api.HasRoleAuth = func(tokenstring, scopes []string) (*models.Principal, error) {
// The header: Authorization: Bearer {base64 string} (or ?access_token={base 64 string} param) has already// been decoded by the runtime as a tokenapi.Logger("HasRoleAuth handler called")
returnauth.HasRole(token, scopes)
}
// Applies when the Authorization header is set with the Basic schemeapi.IsRegisteredAuth = func(userstring, passwordstring) (*models.Principal, error) {
// The header: Authorization: Basic {base64 string} has already been decoded by the runtime as a// username:password pairapi.Logger("IsRegisteredAuth handler called")
returnauth.IsRegistered(user, password)
}
// Applies when the "X-Custom-Key" header is setapi.IsResellerAuth = func(tokenstring) (*models.Principal, error) {
api.Logger("IsResellerAuth handler called")
returnauth.IsReseller(token)
}
// Applies when the "CustomKeyAsQuery" query is setapi.IsResellerQueryAuth = func(tokenstring) (*models.Principal, error) {
api.Logger("ResellerQueryAuth handler called")
returnauth.IsReseller(token)
}
The basic-auth authorizer is a plain credential check returning a typed
principal:
// IsRegistered determines if the user is properly registered,// i.e if a valid username:password pair has been provided.funcIsRegistered(user, passstring) (*models.Principal, error) {
password, ok:=userDb[user]
if !ok||pass!=password {
returnnil, errors.New(401, "Unauthorized: not a registered user")
}
return&models.Principal{
Name: user,
}, nil}
The scoped HasRole authorizer goes further: it parses the JWT, then checks the
token’s claimed roles against the scopes the runtime passed in β the mechanism
that makes hasRole: [ customer ] in the spec actually mean something.
Trying it
Generate test keys and JWTs, then exercise the composed requirements:
$ cd hack/tools && go run . gen-tokens # RSA keypair + role JWTs
$ ./composed-auth/exerciser.sh # sends a sequence of curl requests
OAuth2 access-code β a real OAuth2 handshake (vs. JWT-scope extraction here).
OAuth2 access-code
The other auth examples validate a token the client already has. This one runs
the full OAuth2 access-code handshake β redirecting the user to an identity
provider (Google), receiving a callback, exchanging the code for a token, then
using that token to authenticate API calls.
Tip
Source: oauth2/.
Generated with swagger generate server --name oauthSample --spec ./swagger.yml --principal models.Principal.
The handshake lives in the hand-written restapi/implementation.go.
The scheme
The spec declares an oauth2 scheme with the accessCode flow and its
authorization/token URLs. Unlike composed-auth β which only
borrows the oauth2 type to carry scopes β here the URLs are real and drive an
actual handshake:
go-swagger does not implement the OAuth2 workflow for you: the generator produces
the authenticator hook and the routing, but the redirect/callback/exchange dance
is application code. That’s exactly what this example provides.
Step 1 β redirect to the provider
The public /login endpoint sends the user to Google’s consent screen, using the
golang.org/x/oauth2 config built in implementation.go:
funclogin(r*http.Request) middleware.Responder {
// implements the login with a redirectionreturnmiddleware.ResponderFunc(
func(whttp.ResponseWriter, _runtime.Producer) {
http.Redirect(w, r, config.AuthCodeURL(state), http.StatusFound)
})
}
Step 2 β handle the callback and exchange the code
Google redirects back to /auth/callback with a state and a code. The
handler verifies state, then exchanges the code for an access token via the
oauth2 client:
funccallback(r*http.Request) (string, error) {
// we expect the redirected client to call us back// with 2 query params: state and code.// We use directly the Request params here, since we did not// bother to document these parameters in the spec.ifr.URL.Query().Get("state") !=state {
log.Println("state did not match")
return"", errors.New("state did not match")
}
myClient:=&http.Client{}
parentContext:=context.Background()
ctx:=oidc.ClientContext(parentContext, myClient)
authCode:=r.URL.Query().Get("code")
log.Printf("Authorization code: %v\n", authCode)
// Exchange converts an authorization code into a token.// Under the hood, the oauth2 client POST a request to do so// at tokenURL, then redirects...oauth2Token, err:=config.Exchange(ctx, authCode)
iferr!=nil {
log.Println("failed to exchange token", err.Error())
return"", errors.New("failed to exchange token")
}
// the authorization server's returned tokenlog.Println("Raw token data:", oauth2Token)
returnoauth2Token.AccessToken, nil}
Every protected endpoint runs through the generated OauthSecurityAuth hook. It
validates the bearer token (here by calling Google’s userinfo endpoint) and
returns the principal β the token string itself in this minimal example:
Register an OAuth client at the Google credentials console,
set the callback URL to http://127.0.0.1:12345/api/auth/callback, and put the
resulting client ID/secret into the var block of implementation.go. Then:
$ go run ./oauth2/cmd/oauth-sample-server/main.go --port 12345
# open http://127.0.0.1:12345/api/login in a browser, log in, copy the token
$ curl -i -H 'Authorization: Bearer <TOKEN>' http://127.0.0.1:12345/api/customers
Swagger 2.0 has no first-class notion of a streaming response, but you can still
generate a server that streams. The stream-server example is a countdown API:
GET /elapse/{length} emits one newline-delimited JSON object per second until it
reaches zero.
Tip
Source: stream-server/.
Generated with swagger generate server --spec ./swagger.yml.
Declaring a streaming response
The trick is the response schema: type: string, format: binary. That’s the
closest Swagger 2.0 gets to “this endpoint streams bytes”, and it makes the
generator produce a response the handler writes to directly rather than a typed
payload it serializes for you:
responses:
200:
description: Secondly update on remaining time# This is the best representation there is in Swagger 2.0 to# say that this endpoint has a streaming response.# In this implementation, it will be newline delimited JSON# bodies, of the `Mark` type defined belowschema:
type: stringformat: binary403:
description: Contrived - thrown when length of 11 is chosen
Instead of returning a generated responder, the handler returns a
middleware.ResponderFunc β a closure with raw access to the
http.ResponseWriter. It grabs the http.Flusher and writes through a small
wrapper so each write is pushed to the client immediately:
The flushWriter is what turns a normal write into a streamed chunk β it flushes
after every write, so the client sees each line as it’s produced rather than at
the end:
// Via https://play.golang.org/p/PpbPyXbtEstypeflushWriterstruct {
fhttp.Flusherwio.Writer}
// Via https://play.golang.org/p/PpbPyXbtEsfunc (fw*flushWriter) Write(p []byte) (nint, errerror) {
n, err = fw.w.Write(p)
iffw.f!=nil {
fw.f.Flush()
}
return}
The business logic just encodes one Mark per iteration into the writer, with a
one-second pause between them. Because the writer flushes on every Encode, each
{"remains":N} line reaches the client in real time:
// Down is the concrete implementation that spits out the JSON bodies.func (mc*MyCounter) Down(maximumint64, wio.Writer) error {
ifmaximum==11 {
returnerrors.New("we don't *do* elevensies")
}
e:=json.NewEncoder(w)
forix:= int64(0); ix<=maximum; ix++ {
r:=maximum-ixfmt.Printf("Iteration %d\n", r)
iferr:=e.Encode(models.Mark{Remains: &r}); err!=nil {
returnerr }
ifix!=maximum {
time.Sleep(1*time.Second)
}
}
returnnil}
$ go run ./stream-server/cmd/countdown-server --port=8000
$ curl -N http://127.0.0.1:8000/elapse/5
{"remains":5}
{"remains":4}
{"remains":3}
{"remains":2}
{"remains":1}
{"remains":0}
The response uses Transfer-Encoding: chunked; each line arrives a second apart.
A length of 11 returns 403 (a contrived error to show non-streaming responses
still work normally).
Related
Streaming client β consuming this stream from a generated client.
Hand-wiring a streaming server without codegen? See the
go-openapi/runtime examples.
Streaming client
A generated client normally reads the entire response body and unmarshals it
into a typed payload. To consume a stream you override that behavior β swap the
consumer, and either buffer the whole thing or read it chunk-by-chunk. Two
examples show both approaches.
The generated client hands the response body to a consumer, which does an
io.Copy into the destination you pass. With the default JSONConsumer that
copy blocks until the body is complete and then unmarshals β exactly what you
don’t want for a stream. The fix is to install a ByteStreamConsumer for the
response mime, so the bytes flow through untouched.
Non-blocking: consume chunks as they arrive
Override the consumer, then pass an io.Pipe writer as the destination. The
client’s io.Copy writes into the pipe while a goroutine reads the other end β
so bytes are processed the moment they arrive:
A bufio.Scanner on the read end splits the stream on newlines and unmarshals
each line independently. cancel() (via defer) tears down the request if the
reader stops early:
wg.Add(1)
gofunc(wg*sync.WaitGroup) {
deferwg.Done()
defercancel()
// read response items line by lineforscanner.Scan() {
// each response item is JSONtxt:=scanner.Text()
log.Printf("received countdown mark - raw: %s", txt)
varmarkmodels.Markerr:=json.Unmarshal([]byte(txt), &mark)
iferr!=nil {
log.Printf("unmarshal error: %v", err)
return }
log.Printf("received countdown mark - remaining: %d", conv.Value(mark.Remains))
}
iferr:=scanner.Err(); err!=nil {
log.Printf("scanner err: %v", err)
}
log.Println("EOF")
}(&wg)
If you don’t need incremental processing, the simplest path is a destination that
knows how to accept text/plain. Give a buffer an UnmarshalText method and the
default consumer will fill it:
// Buffer knows how to UnmarshalTexttypeBufferstruct {
*bytes.Buffer}
// UnmarshalText handles text/plainfunc (b*Buffer) UnmarshalText(text []byte) error {
_, err:=b.Write(text)
returnerr}
Then pass it straight to the operation and read it once the call returns:
funcchunkedBlocking(withChunksbool) error {
c:=client.New(customTransport(withChunks), nil).Operations// we just need to specify a buffer that knows how to UnmarshalText()buf:=NewBuffer()
_, err:=c.Chunked(operations.NewChunkedParams(), buf)
iferr!=nil {
returnerr }
data, err:=io.ReadAll(buf)
iferr!=nil {
returnerr }
log.Printf("result: %v", string(data))
returnnil}
The jigsaw.go example also has a non-blocking variant that installs
transport.Consumers[runtime.TextMime] = runtime.ByteStreamConsumer() β the same
technique as above, for a text/plain stream instead of newline-delimited JSON.
Choosing an approach
Blocking + UnmarshalText β least code; fine when you can wait for the full
response.
Non-blocking + ByteStreamConsumer + io.Pipe β process items as they
arrive, cancel early, bound with a context. Use this for long-lived or unbounded
streams.
Related
Streaming server β the countdown server elapsed_client.go consumes.
go-swagger renders code from Go templates, and you can swap in your own. The
contributed-templates example uses the built-in --template stratoscale
option β a community template set that produces a different, interface-first
shape optimized for testability.
Tip
Source: contributed-templates/stratoscale/.
Generated with swagger generate server -A Petstore --template stratoscale (and the
matching swagger generate client --template stratoscale).
What the template changes
Instead of the default’s per-operation handler func fields, the stratoscale
template groups operations into interfaces β one per tag β that your code
implements, and emits //go:generate mockery directives so those interfaces are
trivially mockable in tests:
//go:generate mockery --name PetAPI --inpackage// PetAPItypePetAPIinterface {
// PetCreate Add a new pet to the storePetCreate(ctxcontext.Context, paramspet.PetCreateParams) middleware.Responder// PetDelete Deletes a petPetDelete(ctxcontext.Context, paramspet.PetDeleteParams) middleware.Responder// PetGet Get pet by it's IDPetGet(ctxcontext.Context, paramspet.PetGetParams) middleware.Responder// PetList List petsPetList(ctxcontext.Context, paramspet.PetListParams) middleware.Responder// PetUpdate Update an existing petPetUpdate(ctxcontext.Context, paramspet.PetUpdateParams) middleware.Responder// PetUploadImage uploads an imagePetUploadImage(ctxcontext.Context, paramspet.PetUploadImageParams) middleware.Responder}
Every handler is context-first (ctx context.Context, params β¦), matching the
stratoscale client flavor. You provide
one implementation per interface (PetAPI, StoreAPI, β¦) rather than assigning
individual handler funcs.
When to use it
Reach for a custom template set when the default output doesn’t fit your codebase
conventions β here, interface-based handlers plus generated mocks. Because it’s a
whole template family, it changes server and client output consistently.
To go further, --template-dir points the generator at your own template
directory, letting you override any individual template go-swagger ships.
Related
Generated client SDK β the stratoscale client interface, side by side with the classic one.
By default every schema in your spec becomes a generated Go struct. Sometimes you
want a schema to map onto a type you already have β a hand-written type, one from
another package, or a shared domain type. The external-types example shows how
x-go-type binds a schema to an externally defined Go type instead of generating
one.
Attach x-go-type to a schema to name the Go type it should use, and where to
import it from. Here a property is bound to MyAlternateInteger from the fred
package instead of getting a generated type:
gamma:
description: | Property defined as an external type from package "fred"x-go-type:
type: MyAlternateIntegerimport:
package: "github.com/go-swagger/examples/external-types/fred"
The import.package (and optional alias) tell the generator which import to add.
The generated code references your type directly β no definition is emitted for it.
The generated result
A definition bound to an external type collapses to exactly that type, with the
external package imported (and its name mangled to avoid collisions). This
MyExtCollection is a slice of an external go-ext type:
// MyExtCollection This type demonstrates the import generation with name mangling//
// swagger:model MyExtCollectiontypeMyExtCollection []go_ext.MyExtType
Because the external type is expected to satisfy the runtime’s Validatable
interface, the generated Validate still calls into it per item β so your type
participates in validation like any generated model.
What it covers
The example exercises the full range of external-type use cases:
an external type as its own definition, or nested inside an object/slice/map/tuple;
types pulled from the default models package or from an arbitrary import path;
embedding an external type to add the Validatable interface;
annotation hints to resolve nullable/struct-vs-interface questions and to skip
validation of an external type.
Note
The example spec adds an additionalItems clause to demonstrate tuples, which
makes it not strictly valid against the Swagger 2.0 meta-schema β intentional, to
show the tuple binding.
The generated main.go for a server isn’t fixed β a couple of flags change how
it parses command-line options and whether it carries the spec inside the binary.
The flags example generates the same API six ways so you can compare the
results side by side.
Tip
Source: flags/.
Six sub-packages (pflag/, flag/, go-flags/ and their x⦠variants) are
each generated from one swagger.yml with a different flag combination.
--flag-strategy β how the server parses CLI options
The flag strategy selects the library the generated main.go uses for its
command-line flags. The API is identical; only the flag plumbing differs:
(mkdir pflag && cd pflag && swagger generate server --spec=../swagger.yml --flag-strategy=pflag)
All three expose the same server options β listeners (--scheme), timeouts,
TLS settings, socket path β just rendered in each library’s idiom. Pick the one
that matches the rest of your CLI.
--exclude-spec β embedded vs. runtime spec
By default the spec is embedded in the generated binary (the x⦠variants
drop this):
default (embedded) β the spec is baked in; the server is fully
self-contained and serves its own swagger.json.
--exclude-spec β the spec is not embedded. An extra --spec CLI flag
appears so the server loads the document at startup instead.
Embed for a single shippable artifact; exclude when you want to swap the spec
without rebuilding, or to keep the binary small.
Trying it
Build any variant’s server and ask for help to see that strategy’s flag layout:
$ go build -o srv ./flags/pflag/cmd/simple-to-do-list-api-server && ./srv -h
Related
Custom templates β change the generated code shape, not just its flags.
CLI client β a different use of flags: a generated cobra command-line client.
Custom middleware
A generated server is a standard net/http stack, so any http.Handler
middleware composes with it. The middleware example wires two real concerns β
security response headers and Prometheus metrics β into a generated server using
only the hook points codegen already leaves for you. No --exclude-main and no
custom router required.
Tip
Source: middleware/.
Generated with swagger generate server -A Greeter -f ./swagger.yml. Answers
go-swagger issues #2683
(security headers) and #1120
(a /metrics endpoint).
Two extension points
Codegen leaves two hooks in restapi/configure_*.go, and they run at different
stages of the request:
Hook
Runs
Sees the matched route?
Use it for
setupGlobalMiddleware
Before swagger routing β wraps everything (spec, UI, all routes)
No
Cross-cutting concerns: security headers, panic recovery, a metrics mount
setupMiddlewares
After routing β only matched operations
Yes (middleware.MatchedRouteFrom)
Per-route concerns: instrumentation labelled by route template
The global hook β headers + metrics mount
setupGlobalMiddleware wraps the entire server. Reading outermost to innermost:
metrics.Mount intercepts GET /metrics so scrape traffic bypasses routing;
unrolled/secure adds HSTS and other headers to every response, including the
spec and UI:
// setupGlobalMiddleware wraps everything the server serves, including the// swagger spec document and the embedded UI.//
// Order matters. From outermost to innermost://
// 1. metrics.Mount intercepts GET /metrics so scrape traffic bypasses the// swagger router as well as the headers and instrumentation below.// 2. unrolled/secure injects HSTS and other security response headers on// every response, including /swagger.json and the UI assets.// 3. The swagger handler itself (which will pass through setupMiddlewares// once a route is matched).funcsetupGlobalMiddleware(handlerhttp.Handler) http.Handler {
sec:=secure.New(secure.Options{
STSSeconds: 63072000,
STSIncludeSubdomains: true,
// ForceSTSHeader: true emits HSTS even on plain-HTTP requests so the// example is easy to exercise with `curl http://...`. In production// you would either serve over HTTPS directly (the library detects it// from the TLS connection) or set SSLProxyHeaders so detection works// behind a TLS-terminating proxy β and leave ForceSTSHeader at false.ForceSTSHeader: true,
FrameDeny: true,
ContentTypeNosniff: true,
ReferrerPolicy: "no-referrer",
// Send "X-XSS-Protection: 0" explicitly. The default BrowserXssFilter// option sends "1; mode=block", which is now considered harmful in// some browsers; modern OWASP / MDN guidance is to disable the// legacy XSS auditor outright. See README.CustomBrowserXssValue: "0",
})
returnmetrics.Mount(sec.Handler(handler))
}
setupMiddlewares runs after routing, so the matched route is available. That’s
what lets metrics be labelled by route:
// setupMiddlewares wraps the swagger handler after routing.//
// At this point, [middleware.MatchedRouteFrom] returns the matched route, so// this is the right place to plug per-route instrumentation. Requests that do// not match an operation (404/405) never reach this layer; that is by design// for an example whose metrics describe API operation behaviour.funcsetupMiddlewares(handlerhttp.Handler) http.Handler {
returnmetrics.Instrument(handler)
}
The one piece that’s specific to a go-swagger server is the metrics route
label. It uses middleware.MatchedRouteFrom(r).PathPattern β the swagger path
template (/greet/{name}) rather than the literal path (/greet/alice) β so
Prometheus label cardinality stays bounded instead of exploding one series per
distinct URL:
// Instrument records request count and latency for the wrapped handler.//
// It is meant to be installed in the generated server's setupMiddlewares hook// (i.e. after swagger routing) so that [middleware.MatchedRouteFrom] returns// the matched route. The route label is the swagger path template (e.g.// "/greet/{name}") rather than the literal request path, to keep the metric// label cardinality bounded.funcInstrument(nexthttp.Handler) http.Handler {
returnhttp.HandlerFunc(func(whttp.ResponseWriter, r*http.Request) {
start:=time.Now()
rec:=&statusRecorder{ResponseWriter: w, status: http.StatusOK}
next.ServeHTTP(rec, r)
route:="unmatched"ifmr:=middleware.MatchedRouteFrom(r); mr!=nil&&mr.PathPattern!="" {
route = mr.PathPattern }
obs:=prometheus.Labels{
labelMethod: r.Method,
labelRoute: route,
labelCode: strconv.Itoa(rec.status),
}
requestsTotal.With(obs).Inc()
requestDuration.With(obs).Observe(time.Since(start).Seconds())
})
}
$ go run ./middleware/cmd/greeter-server --port 8080
$ curl -i http://127.0.0.1:8080/api/greet
HTTP/1.1 200 OK
Strict-Transport-Security: max-age=63072000; includeSubDomains
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
...
{"message":"hello"}
$ curl http://127.0.0.1:8080/metrics
http_requests_total{code="200",method="GET",route="/api/greet/{name}"} 3
The route label carries the basePath (/api) because the matched template is
the full path the router serves.
Other middleware, same pattern
Any http.Handler middleware composes identically via setupGlobalMiddleware β
panic recovery (gorilla/handlers.RecoveryHandler), a request ID, an access log,
and so on. The example keeps to headers + metrics to stay focused on the wiring.
Generation flags β --exclude-spec and flag strategies (no --exclude-main needed here).
Alias compatibility
Info
This example runs the other direction β code β spec (swagger generate spec), not spec β code. That code-first workflow is the subject of the
go-openapi/codescan site; it lives here
only because the repo hosts the example. The rest of this site is spec-first
codegen.
The alias-compatibility example shows how a Go type alias is reflected
when you generate a spec from Go code, and how the --transparent-aliases flag
controls it.
This tutorial walks you through a hypothetical project, building a todo list.
It uses a todo list because this is a well-understood application, so you can
focus on the go-swagger pieces. Here we build the server; when you’re done,
head to the client SDK tutorial to generate a typed client
against the same spec.
Info
You’ll need the swagger CLI on your PATH. See
goswagger.io for installation, and the
command reference for the full set of
generate options. The finished code for each stage of this tutorial lives under
tutorials/todo-list/
in the examples repository (server-1, server-2, server-complete).
To create your application start with swagger init:
swagger init spec \
--title "A Todo list application"\
--description "From the todo list tutorial on goswagger.io"\
--version 1.0.0 \
--scheme http \
--consumes application/io.goswagger.examples.todo-list.v1+json \
--produces application/io.goswagger.examples.todo-list.v1+json
In this model definition we say that the model item is an object with a required property description. This item model has 3 properties: id, description, and completed. The id property is an int64 value and is marked as readOnly, meaning that it will be provided by the API server and it will be ignored when the item is created.
This document also says that the description must be at least 1 char long, which results in a string property that’s not a pointer.
At this moment you have enough so that actual code could be generated, but let’s continue defining the rest of the API so that the code generation will be more useful. Now that you have a model so you can add some endpoints to list the todo’s:
paths:
/:
get:
tags:
- todosparameters:
- name: sincein: querytype: integerformat: int64 - name: limitin: querytype: integerformat: int32default: 20responses:
200:
description: list the todo operationsschema:
type: arrayitems:
$ref: "#/definitions/item"
With this new version of the operation you now have query params. These parameters have defaults so users can leave them off and the API will still function as intended.
However, this definition is extremely optimistic and only defines a response for the “happy path”. It’s very likely that the API will need to return errors too. That means you have to define a model errors, as well as at least one more response definition to cover the error response.
At this point you’ve defined your first endpoint completely. To improve the strength of this contract you could define responses for each of the status codes and perhaps return different error messages for different statuses. For now, the status code will be provided in the error message.
Try validating the specification again with swagger validate ./swagger.yml to ensure that code generation will work as expected. Generating code from an invalid specification leads to unpredictable results.
You can see that the files under restapi/operations/todos now use the operationId as part of the generated file names.
At this point can start the server, but first let’s see what --help gives you. First install the server binary and then run it:
Β± ~/go/src/.../examples/tutorials/todo-list/server-1
Β» go install ./cmd/todo-list-server/
Β± ~/go/src/.../examples/tutorials/todo-list/server-1
Β» todo-list-server --help
Usage:
todo-list-server [OPTIONS]From the todo list tutorial on goswagger.io
Application Options:
--scheme= the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec
--cleanup-timeout= grace period for which to wait before shutting down the server (default: 10s) --max-header-size= controls the maximum number of bytes the server will read parsing the request header's keys and values, including the
request line. It does not limit the size of the request body. (default: 1MiB)
--socket-path= the unix socket to listen on (default: /var/run/todo-list.sock)
--host= the IP to listen on (default: localhost) [$HOST]
--port= the port to listen on for insecure connections, defaults to a random value [$PORT]
--listen-limit= limit the number of outstanding requests
--keep-alive= sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)
(default: 3m)
--read-timeout= maximum duration before timing out read of the request (default: 30s)
--write-timeout= maximum duration before timing out write of the response (default: 60s)
--tls-host= the IP to listen on for tls, when not specified it's the same as --host [$TLS_HOST] --tls-port= the port to listen on for secure connections, defaults to a random value [$TLS_PORT] --tls-certificate= the certificate to use for secure connections [$TLS_CERTIFICATE] --tls-key= the private key to use for secure connections [$TLS_PRIVATE_KEY] --tls-ca= the certificate authority file to be used with mutual tls auth [$TLS_CA_CERTIFICATE] --tls-listen-limit= limit the number of outstanding requests
--tls-keep-alive= sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download) --tls-read-timeout= maximum duration before timing out read of the request
--tls-write-timeout= maximum duration before timing out write of the response
Help Options:
-h, --help Show this help message
If you run your application now it will start on a random port by default. This might not be what you want, so you can configure a port through a command line argument or a PORT env var.
git:(master) β !? Β» todo-list-server
serving todo list at http://127.0.0.1:64637
HTTP/1.1501Not ImplementedContent-Type:application/io.goswagger.examples.todo-list.v1+jsonDate:Thu, 31 Dec 2015 22:42:10 GMTContent-Length:57"operation todos.FindTodos has not yet been implemented"
As you can see, the generated API isn’t very usable yet, but we know it runs and does something. To make it useful you’ll need to implement the actual logic behind those endpoints. And you’ll also want to add some more endpoints, like adding a new todo item and updating an existing item to change its description or mark it completed.
To supporting adding a todo item you should define a POST operation:
This time you’re defining a parameter that is part of the path. This operation will look in the URI templated path for an id. Since there’s nothing to return after a delete, the success response is 204 No Content.
Finally, you need to define a way to update an existing item:
This is a good time to sanity check and by validating the schema:
Β± ~/go/src/github.com/go-swagger/examples/tutorials/todo-list/server-2
git:(master) β !? Β» swagger validate ./swagger.yml
The swagger spec at "./swagger.yml" is valid against swagger specification 2.0
Now you’re ready to generate the API and start filling in the actual operations:
To implement the core of your application you start by editing restapi/configure_todo_list.go. This file is safe to edit. Its content will not be overwritten if you run swagger generate again the future.
The simplest way to implement this application is to simply store all the todo items in a golang map. This provides a simple way to move forward without bringing in complications like a database or files.
To do this you’ll need a map and a counter to track the last assigned id:
// the variables we need throughout our implementationvaritems = make(map[int64]*models.Item)
varlastIDint64
The simplest handler to implement now is the delete handler. Because the store is a map and the id of the item is provided in the request it’s a one liner.
After deleting the item from the store, you need to provide a response. The code generator created responders for each response you defined in the swagger specification, and you can see how one of those is being used in the example above.
The other 3 handler implementations are similar to this one. They are provided in the
source for this tutorial.
So assuming you go ahead and implement the remainder of the endpoints, you’re all set to test it out:
Β» curl -i localhost:8765
HTTP/1.1200OKContent-Type:application/io.goswagger.examples.todo-list.v1+jsonDate:Fri, 01 Jan 2016 19:56:01 GMTContent-Length:3[]
Β» curl -i localhost:8765 -d "{\"description\":\"message $RANDOM\"}"
HTTP/1.1415Unsupported Media TypeContent-Type:application/io.goswagger.examples.todo-list.v1+jsonDate:Fri, 01 Jan 2016 19:56:11 GMTContent-Length:157{"code":415,"message":"unsupported media type \"application/x-www-form-urlencoded\", only [application/io.goswagger.examples.todo-list.v1+json] are allowed"}
~/go/src/github.com/go-swagger/examples/tutorials/todo-list/server-complete
Browse the servers guides for variations: strict
handlers, custom error handling, file serving and more.
Client SDK tutorial
The todo-list tutorial built a server from a spec. This one
takes the same kind of spec and generates a typed client SDK β one Go method
per operation, with generated parameter and response types β then walks through
actually calling an API with it.
You’ll generate the SDK in two flavors from one spec (the classic go-swagger
client and the leaner stratoscale contributed template) and see how the
generated signature copes with two spec shapes that trip people up: an operation
with multiple success responses, and one with no default response.
Info
You’ll need the swagger CLI on your PATH β see
goswagger.io. The finished code lives
under tutorials/client/
in the examples repository. For a side-by-side reference comparison of the two
flavors, see the generated client SDK guide;
this page is the hands-on walkthrough.
Step 1 β the spec
Start from a todo-list swagger.yml. The only detail that shapes the client here
is the security scheme: the API is protected by an API-key header, so every
request the client sends must carry a x-todolist-token:
That security requirement is what forces an auth writer into the calls below.
Step 2 β generate the classic client
Point swagger generate client at the spec:
swagger generate client -A TodoList --spec swagger.yml --client-package classic_client
This writes a classic_client/ package: a top-level TodoList client whose
fields group the operations by tag (Todos, Experimental), plus generated
β¦Params and β¦Responses types under each tag package.
Step 3 β call the API
Instantiate the client over a transport, then call an operation. Because the spec
requires an API key, you pass a runtime.ClientAuthInfoWriter built from the
transport’s APIKeyAuth helper β the classic client takes it per call:
import (
httptransport"github.com/go-openapi/runtime/client""github.com/go-openapi/strfmt""github.com/go-openapi/swag/conv"client"github.com/go-swagger/examples/tutorials/client/classic_client""github.com/go-swagger/examples/tutorials/client/classic_client/todos""github.com/go-swagger/examples/tutorials/client/models")
// point the transport at the running servertransport:=httptransport.New("localhost:8080", "/", []string{"http"})
c:=client.New(transport, strfmt.Default)
// the API-key writer that satisfies the `key` security schemeauth:=httptransport.APIKeyAuth("x-todolist-token", "header", "my-secret-token")
params:=todos.NewAddOneParams().WithBody(&models.Item{
Description: conv.Pointer("write the client tutorial"),
})
created, noContent, err:=c.Todos.AddOne(params, auth)
Notice the call returns three values, not two β that’s the next step.
Step 4 β the stratoscale flavor
Regenerate the same spec with the stratoscale template for a leaner, context-first
client (auth folded into construction, no per-call options, and a
//go:generate mockery directive for easy mocking):
Same operation, same three return values β only the ergonomics differ. Pick
classic for the full go-swagger surface (per-call auth and options); pick
stratoscale for a compact, mockable client.
Step 5 β multiple success responses
Why three return values? Because addOne declares two success responses in
the spec β 201 Createdand204 No Content:
The generated method reflects that by handing back a pointer for each possible
success; exactly one is non-nil. Switch on them:
created, noContent, err:=c.Todos.AddOne(params, auth)
switch {
caseerr!=nil:
// transport failure, or a typed error responsecasecreated!=nil:
// 201 β the new item is in created.Payloadlog.Printf("created #%d", created.Payload.ID)
casenoContent!=nil:
// 204 β the item already existed; nothing to readlog.Print("already there")
}
The no default response case (the experimental operations declare 401/405
but no default) works by the same mechanism in reverse: with no default, any
status code the spec didn’t list can’t map to a typed payload, so it surfaces as a
generic error from the response reader rather than a *β¦Default value.
The todo-list tutorial generated a whole server β main.go
and all β and you edited the configure_*.go file it left for you. Sometimes you
want the opposite balance: keep go-swagger’s generated core (the models,
router, and typed operations) but own the main yourself, so the CLI is a thin
hand-written layer that wires configuration and handlers around that core.
That’s what --exclude-main is for. This tutorial builds a tiny greeter server
that way.
The greeter is deliberately minimal: one GET /hello that takes an optional
name query parameter and returns a plain-text greeting.
swagger: '2.0'info:
version: 1.0.0title: Greeting Serverpaths:
/hello:
get:
produces:
- text/plainparameters:
- name: namerequired: falsetype: stringin: querydescription: defaults to World if not givenoperationId: getGreetingresponses:
200:
description: returns a greetingschema:
type: stringdescription: contains the actual greeting as plain text
Step 2 β generate the core only
Generate the server into a gen/ sub-tree with --exclude-main, so go-swagger
emits everything except a main.go:
rm -rf gen && mkdir gen
swagger generate server --exclude-main -A greeter -t gen -f ./swagger/swagger.yml
You get gen/restapi/ β the embedded spec, the NewServer constructor, the
router, and operations/ with the typed GreeterAPI, its GetGreetingParams,
and the GetGreetingOK responder. What you don’t get is a cmd/ entry point.
That’s yours to write.
Step 3 β write your own main
Your main does what the generated main.go would have β load the embedded spec,
construct the API, and hand it to NewServer β but it’s plain code you control:
// load embedded swagger fileswaggerSpec, err:=loads.Analyzed(restapi.SwaggerJSON, "")
iferr!=nil {
returnerr}
// create new service APIapi:=operations.NewGreeterAPI(swaggerSpec)
server:=restapi.NewServer(api)
Because you own this file, you can add your own flags, config loading, dependency
injection, logging, or lifecycle management around this core β none of it is
generated, none of it gets overwritten on regeneration.
Step 4 β attach the handler
The generated GreeterAPI exposes one handler field per operation. Assign your
implementation to GetGreetingHandler before serving β this is the same handler
you’d otherwise place in a generated configure_*.go, but here it lives in your
main:
// GetGreetingHandler greets the given name,// in case the name is not given, it will default to Worldapi.GetGreetingHandler = operations.GetGreetingHandlerFunc(
func(paramsoperations.GetGreetingParams) middleware.Responder {
name:=conv.Value(params.Name)
ifname=="" {
name = "World" }
greeting:=fmt.Sprintf("Hello, %s!", name)
returnoperations.NewGetGreetingOK().WithPayload(greeting)
})
conv.Value dereferences the optional *string parameter, defaulting to World,
and NewGetGreetingOK().WithPayload(...) returns the typed 200 responder the
generated code defined.
$ http get :3000/hello # Hello, World!
$ http get :3000/hello name==Swagger # Hello, Swagger!
Regenerating safely
The whole point of the split is that regeneration only ever touches gen/. When
the spec changes, rerun the Step 2 command β gen/ is rewritten, your cmd/main is untouched. Keep the two apart (generated core under gen/, your code
outside it) and the two never collide.
Related
Todo list tutorial β the opposite balance: generate the whole server and edit configure_*.go.
Custom middleware guide β extend a fully generated server via its hook points, no --exclude-main needed.
This site documents the go-swagger/examples
repository β a collection of runnable, committed examples for
go-swagger, the spec-first code
generator for OpenAPI 2.0 (Swagger).
What’s here
Every example is a real Go project generated from an OpenAPI 2.0 spec: servers,
typed client SDKs, and CLIs. The generated code is committed and kept in sync
with go-swagger master by automated regeneration, so what you
read on this site matches what the current generator emits.
Browse the material two ways:
Guides β reference recipes you dip into (servers,
clients & CLI, authentication, streaming, customizing codegen).
Tutorials β sequential, end-to-end walkthroughs that build
something from scratch.
Where this fits β three sibling sites
go-swagger and go-openapi split their example material across three sites by
workflow. This one is the spec-first corner: you write an OpenAPI spec and
generate typed Go from it.
Site
Workflow
You start from
this site
spec-first codegen
an OpenAPI 2.0 spec β generated server/client/CLI
When a topic straddles two workflows, the page links across.
Getting started
git clone https://github.com/go-swagger/examples
You’ll need the swagger CLI on your PATH to regenerate or follow the
tutorials β see the installation instructions.
Then head to the todo-list tutorial to build a server
and client from one spec.
Status & releasing
The examples track go-swagger code generation for servers and clients. The
repository is deliberately left unreleased: it follows the generator on
go-swagger/go-swagger@master rather than tagging versions of its own.
The header is part of the contribution rules and is checked in CI. Generated files
carry it too, emitted by the templates, so a regeneration never strips it.
Using the examples
Apache-2.0 is a permissive license: you may use, modify, and redistribute the
example code, including in commercial and closed-source work, provided you retain
the license and copyright notices and state significant changes. The code is
provided as-is, without warranty. See the full text for the authoritative
terms.
Contributing
Contributions are always welcome β and not just code. Reporting issues, improving
docs, triaging bugs, and adding test coverage all help. These guidelines are the
standard ones shared across every go-openapi and go-swagger repository; if
you’ve contributed to a Go project on GitHub before, you’ll feel at home.
Fork the repo, branch from master, and open a pull request from your fork.
Branch naming is not enforced (it’s your fork), but the common convention is
fix/XXX-something or feature/XXX-something, where XXX is the issue number.
Keep pull requests focused β small, single-purpose PRs are reviewed faster and
are less likely to lose the thread than large ones.
A special note for generated code
Most of this repository is generated and must not be hand-edited β a
regeneration would silently overwrite your change. If your
contribution affects generated output:
change the spec or the generation command (in hack/tools/regen.go), not
the generated .go files;
run go run ./hack/tools regen and commit the regenerated result;
hand-written glue (the configure_*.go files, custom handlers, main.go in the
custom-server example) is editable β that’s the code these examples exist to
illustrate.
Tests
Submit unit tests for your changes and run the full suite before opening a PR:
go test ./...
CI measures patch coverage; aim for at least 80% of your change. It’s an indicator
maintainers weigh, not a hard gate.
Code style & linting
The project runs the golangci-lint meta-linter with a deliberate posture:
default: all, then disable what doesn’t earn its keep. The disabled list in
.golangci.yml
is a design rationale, not technical debt. Two rules matter most when contributing:
every //nolint directive must carry an inline comment explaining why;
prefer disabling a linter globally over scattering //nolint β if a linter
fights an intentional pattern, the linter goes, not the code.
Add it automatically with git commit -s. PGP-signed commits are appreciated but
not required. Squash your commits into logical units (git rebase -i) before
requesting review.
AI agents
Agentic contributors are welcome, with a few rules:
Issues and PRs written or posted by an agent should mention the original
human poster for reference.
PRs must not be attributed to an agent as author β no commits authored by
@claude.code or similar. Agents and bots may be listed as Co-Authored-By:;
the commit author must be the human sponsor.
Security reports produced by an agent must be filed privately (see the
security policy)
and mention the human poster.
Regeneration
The generated code in this repository is committed, yet it always reflects the
current go-swagger generator. That’s the whole point of the examples: what you
read here is what swagger generate emits from master today, not a frozen
snapshot. A single tool keeps them in sync.
Regenerating everything
go run ./hack/tools regen
The regen command drives the whole repository from one place β
hack/tools/regen.go
holds a table with one entry per example: which directories to clean, and the exact
swagger generate command(s) to run. For each example it:
ensures the generator is present β if swagger isn’t on your PATH, it
installs it from source (go install github.com/go-swagger/go-swagger/cmd/swagger@master),
pinning regeneration to the latest generator;
cleans the generated sub-directories (models, restapi, client, cmd, β¦)
so nothing stale survives;
runs that example’s generate command(s) β server, client, or both, with the
flags and templates each example needs;
restores any preserved hand-written files that live inside a cleaned tree;
finally, once every example is regenerated, runs go test ./... across the whole
module as a smoke test.
Because the command list is data, adding or changing an example’s generation is a
one-line edit to that table β not a shell script to maintain.
Auth material for runnable examples
A few examples need secrets to actually run (TLS certificates, JWT signing keys).
These are deliberately not committed. Generate them locally with the same tool:
go run ./hack/tools gen-certs # self-signed TLS certs (todo-list-errors)go run ./hack/tools gen-tokens # RSA keypair + JWT tokens (composed-auth)
Automated regeneration in CI
Regeneration also runs unattended, so the committed code never drifts from the
generator:
Workflow
Trigger
Purpose
regen.yml
Weekly (Mon 06:00 UTC) + manual
Regenerate from swagger@master; open an auto-merged PR if output changed
On top of the weekly job, go-swagger itself triggers a cross-repo pipeline:
PRs to go-swagger that touch code generation open a regeneration PR here
automatically, so a generator change and its effect on the examples are reviewed
together.
Related
Contributing β why you edit the spec or the regen table, never the generated files.