# Where does /openapi/v1.json actually come from?

### Introduction

If you’ve worked with [ASP.NET](http://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](http://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](http://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**:

```csharp
/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**:

1. OpenAPI services read the **cached controller metadata**
    
2. The OpenAPI JSON spec is built **on-demand**
    
3. 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()`?

```csharp
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.json` is **not a file**
    
* It’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.
