Serve Swagger UI with Python's built-in HTTP server
Hosts a self-contained Swagger UI with a mock OpenAPI spec using only Python's standard library HTTP server.
Python code
54 linesfrom http.server import HTTPServer, SimpleHTTPRequestHandler
import os
import tempfile
SWAGGER_HTML = """<!DOCTYPE html>
<html>
<head>
<title>Mock Swagger UI</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@4/swagger-ui.css">
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@4/swagger-ui-bundle.js"></script>
<script>
SwaggerUIBundle({
url: "/openapi.json",
dom_id: "#swagger-ui",
});
</script>
</body>
</html>
"""
OPENAPI_JSON = """{
"openapi": "3.0.0",
"info": {"title": "Mock API", "version": "1.0.0"},
"paths": {
"/health": {
"get": {"summary": "Health check", "responses": {"200": {"description": "OK"}}}
}
}
}"""
class MockHandler(SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == "/":
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.end_headers()
self.wfile.write(SWAGGER_HTML.encode())
elif self.path == "/openapi.json":
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(OPENAPI_JSON.encode())
else:
super().do_GET()
if __name__ == "__main__":
with tempfile.TemporaryDirectory() as tmpdir:
os.chdir(tmpdir)
server = HTTPServer(("localhost", 8080), MockHandler)
print("Serving Swagger UI on http://localhost:8080")
server.serve_forever()
Output
Serving Swagger UI on http://localhost:8080
When you open http://localhost:8080 in a browser, the page renders the Swagger UI with the /health endpoint from openapi.json.
How it works
The SimpleHTTPRequestHandler subclass overrides do_GET to serve two custom routes: / returns an HTML page that loads the Swagger UI from a CDN, and /openapi.json returns a minimal OpenAPI 3.0 spec. By calling super().do_GET() for any other path, the handler falls back to serving static files from the current directory, which we switch to a temporary directory using os.chdir. This keeps the mock entirely self-contained without needing Flask or FastAPI. The HTTPServer with serve_forever() blocks the main thread and listens on localhost:8080, making it perfect for local API contract testing.
Common mistakes
- Forgetting to call `super().do_GET()` for unknown paths, which breaks static file serving.
- Not setting the correct Content-Type header (text/html vs application/json), causing browsers to render raw text.
- Using `http.server` without a temporary directory, leaving stray files in the project.
- Binding to a non-loopback address without proper firewall rules, exposing the server publicly.
Variations
- Use `functools.partial` to pass configurable paths or specs to the handler class.
- Serve from an in-memory dict of routes instead of hard-coded globals for larger specs.
Real-world use cases
- Local API contract validation while developing a client SDK against a mocked OpenAPI spec.
- Spinning up a lightweight mock service for frontend teams to prototype against before the backend is ready.
- Testing API documentation generation tools by pointing them at a temporary OpenAPI endpoint.
Sponsored
More from API design & gRPC
- Build a Bulk Array POST Mock Server in Python medium
- Build a Mock REST API with PUT and GET in Python medium
- Convert Protobuf to JSON and Dict in Python easy
- Create a Data Helper in Python for gRPC-style APIs easy
- Format data in Python using dataclasses like gRPC messages easy
- Generate an OpenAPI Spec from Mock Routes in Python easy
Keep learning
Related tutorials and quizzes for this topic.