Skip to content

Middleware

API Docs

Serve a browsable reference page for your API's OpenAPI spec, with Scalar, Swagger UI, Stoplight Elements or ReDoc.

API Docs serves a reference page for your API that people can read and try requests from. It renders the OpenAPI spec every Zinc app serves at /openapi.json, so the page is always in step with your routes.

Every app already serves this page at /docs, with Scalar. Use API Docs to serve it with another renderer, from your own copy of its files, or behind middleware. Both use the same page code.

import "github.com/0mjs/zinc/middleware/apidocs"
app := zinc.New(zinc.Config{
OpenAPI: zinc.OpenAPIConfig{Title: "Pet Store", Version: "1.0.0"},
DocsPath: "-", // turn off the built-in page
})
app.Get("/docs", apidocs.New(apidocs.Config{UI: apidocs.SwaggerUI})).Hidden()

Open http://localhost:8080/docs in a browser. The page loads Swagger UI from a CDN, which fetches /openapi.json and renders every route, its parameters, bodies and responses.

Terminal window
curl http://localhost:8080/docs
# <!doctype html>
# ...
# <title>API reference</title>
# <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.33.0/swagger-ui.css" integrity="sha384-Ov4/wv3j2bmct8cDc5X4ngJZohVPzEmc6uDPH8WeljUxO5vtoykvMEfbu9Vh6RaW" crossorigin="anonymous">
# </head>
# <body>
# <div id="swagger-ui" data-spec="/openapi.json"></div>
# <script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.33.0/swagger-ui-bundle.js" integrity="sha384-YDALVcy8kj8yltLBVi1vBiBAUqdxvus673gM8XKwiy6aDUJFXivF/KCufekjYbVf" crossorigin="anonymous"></script>
# <script>SwaggerUIBundle({ url: document.getElementById('swagger-ui').dataset.spec, dom_id: '#swagger-ui' })</script>

.Hidden() keeps the docs route out of the spec it displays. The page is rendered once, when you call New, and every request sends the same bytes.

Setting Default
Spec /openapi.json (apidocs.DefaultSpec)
UI apidocs.Scalar
Title API reference
Assets pinned versions from cdn.jsdelivr.net, each with an integrity hash

Pick another renderer with UI:

app.Get("/docs", apidocs.New(apidocs.Config{
UI: apidocs.SwaggerUI,
Spec: "/api/openapi.json",
Title: "Pet Store API",
})).Hidden()
Field Default Meaning
Spec /openapi.json URL of the spec the page loads. It can be relative to the page or on another host.
UI apidocs.Scalar The renderer: Scalar, SwaggerUI, Stoplight or Redoc.
Title API reference The page’s <title>.
AssetsURL none Load the renderer’s files from this URL on your own server instead of the CDN. See Serve the files yourself.
UI Version Files
apidocs.Scalar @scalar/api-reference 1.72.1 standalone.js
apidocs.SwaggerUI swagger-ui-dist 5.33.0 swagger-ui.css, swagger-ui-bundle.js
apidocs.Stoplight @stoplight/elements 9.0.25 styles.min.css, web-components.min.js
apidocs.Redoc redoc 2.5.4 redoc.standalone.js

Set AssetsURL when the docs must work offline, or when your Content-Security-Policy doesn’t allow the CDN. Download the files apidocs.Files(ui) lists, at the pinned versions, and serve them under that URL:

for _, f := range apidocs.Files(apidocs.Scalar) {
fmt.Println(f.Name, f.URL)
}
// standalone.js https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.72.1/dist/browser/standalone.js
app.Static("/assets/apidocs", "./assets/apidocs") // holds standalone.js
app.Get("/docs", apidocs.New(apidocs.Config{AssetsURL: "/assets/apidocs"})).Hidden()

The page keeps each file’s integrity hash, so a file from a different version doesn’t run.

New panics on an unknown UI, like other setup mistakes. Nothing fails per request: the page is fixed bytes.

When the page loads but shows no routes, the browser couldn’t fetch the spec. Check that Spec matches the path you gave app.OpenAPI, and that any auth on the spec route lets the browser through.

  • Hiding the docs isn’t access control. Anyone who can reach your routes can call them. Protect private APIs with auth middleware, and protect /docs and /openapi.json too if their contents are private. app.OpenAPI serves the spec behind middleware in place of the default: app.OpenAPI("/openapi.json", cfg, requireAPIKey).
  • Scripts from a CDN. Every file is pinned to one version and loaded with a Subresource Integrity hash, so the browser refuses a file that has changed. Serve the files yourself with AssetsURL if you’d rather not depend on the CDN at all.
  • Content-Security-Policy. Scalar and Swagger UI start with a short inline script. It’s the same for every page, so a policy can allow it by hash rather than with 'unsafe-inline'. The renderers also inject styles and load fonts and images, so start from a policy that works in your browser’s console and tighten it from there. Secure Headers sets the header.
  • Secure Headers: send a Content-Security-Policy for your pages.
  • Key Auth: require an API key, including on the docs and spec routes.
  • Static Files: serve the renderer’s files with AssetsURL.