Where does /openapi/v1.json actually come from?
👋 Hey, I'm Ankit Bajpai!
Software engineer building scalable systems and exploring the intersection of clean code, architecture, and AI.
🎯 What I Write About
I document my learning journey through practical, code-heavy articles on:
- .NET & ASP.NET Core - Modern web development with C#
- System Design - LLD patterns, HLD architectures, and scalability
- Clean Code & Architecture - SOLID, design patterns, and maintainable systems
- Gen AI for Developers - LLMs, RAG, and AI-powered development
💡 My Approach
Learn → Build → Document → Share
I learn from books, courses, and real projects, then break down complex concepts into digestible tutorials with working code examples. Think of this as my public learning journal that helps you skip the confusion I faced.
🚀 Currently Exploring
- Clean Architecture in .NET
- LLM integration with Semantic Kernel
- System design interview patterns
- Microservices and event-driven architecture
📬 Let's Connect
Subscribe for weekly deep dives into .NET, system design, and modern software engineering. No fluff—just practical knowledge you can apply immediately.
Happy coding! 🎉
Introduction
If you’ve worked with ASP.NET Core APIs, you’ve definitely hit /openapi/v1.json (or /swagger/v1/swagger.json) at least once.
For a long time, I assumed this file was:
generated at startup, or
stored somewhere on disk, or
magically handled by Swagger UI
Turns out—none of that is true.
Here’s what actually happens under the hood.
🚫 First, a myth to bust
/openapi/v1.json does not live on disk.
There is no physical JSON file sitting inside your project or deployment.
Instead, it’s:
generated on-demand
cached in memory
served only when requested
This design is very intentional.
🔧 What sets everything up?
1️⃣ AddOpenApi()
This registers the OpenAPI document generation services into Dependency Injection.
Think of it as:
“Hey ASP.NET Core, be ready to generate an OpenAPI spec if someone asks.”
No JSON is generated yet.
2️⃣ AddControllers()
This is where most of the heavy lifting actually happens.
At startup, ASP.NET Core:
scans all controllers
reads routes, attributes, HTTP verbs
understands request/response types
caches this metadata internally
This metadata is reused for routing, model binding, validation — and later for OpenAPI.
🌐 How does /openapi/v1.json appear?
3️⃣ MapOpenApi()
This registers a single endpoint:
/openapi/v1.json
Important things to note:
It’s not part of the request pipeline
It doesn’t intercept API calls
It only responds when this exact route is hit
⚡ What happens on the first request?
When someone hits /openapi/v1.json for the first time:
OpenAPI services read the cached controller metadata
The OpenAPI JSON spec is built on-demand
The generated document is cached in memory
No disk IO. No regeneration on every request.
🚀 What about subsequent requests?
After the first request:
The already-generated document is served from memory
Responses are fast
No recomputation happens
So the cost is paid once—only if someone actually asks for it.
🧠 Why not generate it at startup?
Because:
Startup time matters
Many services never expose Swagger/OpenAPI publicly
Generating a document no one requests is wasted work
On-demand + caching is a smart tradeoff.
🛑 Why is it usually inside IsDevelopment()?
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
This is not a performance concern.
It’s a security concern.
Leaving it enabled in production:
exposes your entire API surface
leaks models, routes, and structure
helps attackers understand your system faster
⚠️ Rare edge cases
You might notice a small delay on the first request if:
you have hundreds of controllers
very deep or complex models
heavy use of generics and polymorphism
Even then, we’re talking milliseconds, and only once.
✅ Key takeaways
/openapi/v1.jsonis not a fileIt’s generated on first request
It uses already-cached controller metadata
The result is cached in memory
It has zero impact on normal API requests
Disable it in production for security, not performance
Once you know this, Swagger feels a lot less “magical” and a lot more elegant.