Publish OpenAPI 3.0 Specs with Swagger UI: Contract-First Setup
Write an OpenAPI 3.0 contract, lint it with Spectral, and render interactive docs with Swagger UI before implementing endpoints.
28 Aug 2025, 22:24 UTC

Problem: APIs built without a shared contract
Backend and frontend teams often discover mismatched request and response shapes during integration testing. API documentation drifts from the actual implementation, and QA writes tests against assumptions instead of a single authoritative source. The result is rework, broken clients, and uncertainty about which endpoints exist.
Takeaway: validate the contract before writing endpoints
Adopt a contract-first workflow with OpenAPI 3.0: write the API specification first, lint it with Spectral, and render it with Swagger UI. This gives every team a browsable, machine-readable contract before implementation begins. Swagger UI is the interactive documentation layer; it does not enforce security or prove that code matches the spec.
Prerequisites
- A text editor with YAML support
- Node.js and npm, used to install the Spectral CLI and serve static files locally
- An OpenAPI 3.0 specification file, for example
openapi.yaml - Network access to download npm packages
1. Write a minimal OpenAPI 3.0 specification
OpenAPI 3.0 documents use the openapi: 3.0.x field and organize reusable definitions under components/schemas. The nullable: true keyword is valid in OpenAPI 3.0 but not in OpenAPI 2.0, where x-nullable is used instead. Mixing versions in one file causes parser errors.
openapi: 3.0.1
info:
title: Example Orders API
version: 1.0.0
servers:
- url: https://api.example.com/v1
paths:
/orders:
get:
summary: List orders
operationId: listOrders
security:
- bearerAuth: []
parameters:
- in: query
name: limit
schema:
type: integer
nullable: true
description: Maximum number of orders to return
responses:
'200':
description: A list of orders
content:
application/json:
schema:
$ref: '#/components/schemas/OrderList'
components:
schemas:
OrderList:
type: object
properties:
items:
type: array
items:
$ref: '#/components/schemas/Order'
total:
type: integer
Order:
type: object
required:
- id
- status
properties:
id:
type: string
format: uuid
status:
type: string
enum: [pending, shipped, cancelled]
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
components/securitySchemes documents the intended authentication method. It does not enforce protection; runtime middleware must still validate tokens or API keys.
2. Lint the specification with Spectral
Run the following commands in the project root. Normal user permissions are sufficient.
npm install --save-dev @stoplight/spectral-cli
npx spectral lint openapi.yaml --ruleset spectral:oas3
| Check | Expected result |
|---|---|
| Command exit code | 0 when no structural or style violations are found |
| Console output | No error entries; warnings may appear for non-critical style issues |
| OpenAPI version field | 3.0.x if the spec uses nullable or oneOf |
Risk: lint failures can block continuous integration if the rule is added to a pipeline. A custom ruleset may flag conventions that are not errors, so review failures before treating them as blocking.
3. Serve Swagger UI locally
Swagger UI can be served as static assets. Copy the Swagger UI distribution files into a directory such as ./docs/swagger-ui, place openapi.yaml in the same directory, and open the UI with the url query parameter pointing to the spec.
python3 -m http.server 8080
Run this command inside ./docs. Normal user permissions are sufficient. Open http://localhost:8080/swagger-ui/?url=./openapi.yaml in a browser.
Expected checks:
- All paths declared in the spec appear in the left navigation panel
- Schemas under
components/schemasrender in the Models section - The browser console shows no Swagger UI parser warnings
Risk: the Try it out button sends requests to the server URL in the spec. If that server is not running or does not implement the endpoint, requests will fail. This is expected; Swagger UI is documentation, not a substitute for a running API.
4. Add validation to continuous integration
Add the Spectral lint command to your CI pipeline so that pull requests cannot merge with an invalid OpenAPI document. This catches version mismatches, missing required fields, and schema errors before they reach consumers.
Swagger UI does not prove the implementation matches the spec. Pair the documentation with contract tests or generated stub smoke tests to verify request and response shapes at runtime.
Limitations
- Swagger UI renders documentation only; it does not enforce runtime authentication or authorization.
- OpenAPI 2.0 and 3.0 keywords cannot be mixed in one file. Choose one version and remain consistent.
- Exposing Swagger UI publicly may reveal internal endpoints and schemas. Restrict access with authentication or maintain separate internal and external specifications.
- Generated server stubs or client code can drift from the spec. Regenerate artifacts after every contract change.
Practical verification
After making changes to openapi.yaml, run Spectral again and reload Swagger UI. Confirm that new paths, parameters, and response schemas appear without parser warnings. If you generate client code from the spec, regenerate it and compile against the generated types to detect signature drift.
Recovery
This workflow creates text files and configuration. Rollback means reverting openapi.yaml, CI configuration, or static file changes to the previous known-good commit in version control. Linting and local serving do not modify runtime state or databases.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.