> ## Documentation Index
> Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenAPI

> Generate API documentation automatically from your Contract services

OpenAPI (formerly known as Swagger) is a standard way to describe REST APIs. Contract automatically generates an OpenAPI specification from your service - no manual YAML writing needed!

## What Is OpenAPI?

OpenAPI is a specification format that describes your API:

* What endpoints exist
* What parameters they accept
* What responses they return
* What your data types look like

This specification can be used to:

* **Generate documentation**: Beautiful, interactive API docs with Swagger UI or Redoc
* **Generate client code**: Create typed clients in TypeScript, Python, Go, and more
* **Test your API**: Import into Postman, Insomnia, or other API tools
* **Validate requests**: Ensure requests match the expected format

## Quick Start

Generate and serve OpenAPI documentation:

```go theme={null}
import (
    "github.com/go-mizu/mizu"
    contract "github.com/go-mizu/mizu/contract/v2"
    "github.com/go-mizu/mizu/contract/v2/transport/rest"

    "yourapp/todo"
)

// Your interface is defined in the todo package as todo.API:
// type API interface {
//     Create(ctx context.Context, in *CreateInput) (*Todo, error)
//     List(ctx context.Context) (*ListOutput, error)
//     Get(ctx context.Context, in *GetInput) (*Todo, error)
// }

// Create your service implementation
impl := todo.NewService()

// Register your service
svc := contract.Register[todo.API](impl,
    contract.WithDefaultResource("todos"),
)

app := mizu.New()

// Mount REST endpoints
rest.Mount(app.Router, svc)

// Generate and serve OpenAPI spec
specBytes, _ := rest.OpenAPIDocument(svc)
app.Get("/openapi.json", func(c *mizu.Ctx) error {
    c.Set("Content-Type", "application/json")
    return c.Send(specBytes)
})

app.Listen(":8080")
```

Now visit `http://localhost:8080/openapi.json` to see your API specification.

## What Gets Generated

Contract analyzes your service and generates a complete OpenAPI 3.0.3 specification.

### From Your Interface

```go theme={null}
// todo/api.go
package todo

type API interface {
    Create(ctx context.Context, in *CreateInput) (*Todo, error)
    List(ctx context.Context) (*ListOutput, error)
    Get(ctx context.Context, in *GetInput) (*Todo, error)
}

// todo/types.go
package todo

type CreateInput struct {
    Title       string `json:"title"`
    Description string `json:"description"`
}

type Todo struct {
    ID          string    `json:"id"`
    Title       string    `json:"title"`
    Description string    `json:"description"`
    Completed   bool      `json:"completed"`
    CreatedAt   time.Time `json:"createdAt"`
}

type GetInput struct {
    ID string `json:"id" path:"id"`
}

type ListOutput struct {
    Items []*Todo `json:"items"`
    Total int     `json:"total"`
}
```

### To OpenAPI Specification

```json theme={null}
{
  "openapi": "3.0.3",
  "info": {
    "title": "Todo",
    "version": "0.1.0"
  },
  "paths": {
    "/todos": {
      "post": {
        "operationId": "todos_Create",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateInput" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Todo" }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "todos_List",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ListOutput" }
              }
            }
          }
        }
      }
    },
    "/todos/{id}": {
      "get": {
        "operationId": "todos_Get",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Todo" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CreateInput": {
        "type": "object",
        "properties": {
          "title": { "type": "string" },
          "description": { "type": "string" }
        },
        "required": ["title", "description"]
      },
      "Todo": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "title": { "type": "string" },
          "description": { "type": "string" },
          "completed": { "type": "boolean" },
          "createdAt": { "type": "string", "format": "date-time" }
        },
        "required": ["id", "title", "description", "completed", "createdAt"]
      },
      "ListOutput": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Todo" }
          },
          "total": { "type": "integer", "format": "int32" }
        },
        "required": ["items", "total"]
      }
    }
  }
}
```

## Adding Interactive Documentation

The JSON spec is machine-readable. For human-readable docs, add Swagger UI or Redoc.

### Option 1: Swagger UI

Swagger UI provides an interactive interface where users can try your API:

```go theme={null}
app := mizu.New()
rest.Mount(app.Router, svc)

// Serve OpenAPI spec
specBytes, _ := rest.OpenAPIDocument(svc)
app.Get("/openapi.json", func(c *mizu.Ctx) error {
    c.Set("Content-Type", "application/json")
    return c.Send(specBytes)
})

// Serve Swagger UI at /docs
app.Get("/docs", func(c *mizu.Ctx) error {
    return c.HTML(200, `<!DOCTYPE html>
<html>
<head>
    <title>API Documentation</title>
    <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
</head>
<body>
    <div id="swagger-ui"></div>
    <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
    <script>
        SwaggerUIBundle({
            url: "/openapi.json",
            dom_id: "#swagger-ui"
        });
    </script>
</body>
</html>`)
})

app.Listen(":8080")
```

Now visit `http://localhost:8080/docs` to see interactive documentation!

### Option 2: Redoc

Redoc provides clean, three-panel documentation:

```go theme={null}
app.Get("/docs", func(c *mizu.Ctx) error {
    return c.HTML(200, `<!DOCTYPE html>
<html>
<head>
    <title>API Documentation</title>
</head>
<body>
    <redoc spec-url="/openapi.json"></redoc>
    <script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>
</body>
</html>`)
})
```

## Complete Example

This example shows OpenAPI generation using the recommended package-based organization:

```go theme={null}
// todo/types.go
package todo

import "time"

type Todo struct {
    ID          string    `json:"id"`
    Title       string    `json:"title"`
    Description string    `json:"description"`
    Completed   bool      `json:"completed"`
    CreatedAt   time.Time `json:"createdAt"`
}

type CreateInput struct {
    Title       string `json:"title"`
    Description string `json:"description,omitempty"`
}

type GetInput struct {
    ID string `json:"id" path:"id"`
}

type ListOutput struct {
    Items []*Todo `json:"items"`
    Total int     `json:"total"`
}
```

```go theme={null}
// todo/api.go
package todo

import "context"

// API defines the contract for todo operations
type API interface {
    Create(ctx context.Context, in *CreateInput) (*Todo, error)
    Get(ctx context.Context, in *GetInput) (*Todo, error)
    List(ctx context.Context) (*ListOutput, error)
}
```

```go theme={null}
// todo/service.go
package todo

import (
    "context"
    "fmt"
    "sync"
    "time"

    contract "github.com/go-mizu/mizu/contract/v2"
)

// Service implements todo.API
type Service struct {
    mu     sync.RWMutex
    todos  map[string]*Todo
    nextID int
}

// Compile-time check
var _ API = (*Service)(nil)

func NewService() *Service {
    return &Service{todos: make(map[string]*Todo)}
}

func (s *Service) Create(ctx context.Context, in *CreateInput) (*Todo, error) {
    if in.Title == "" {
        return nil, contract.ErrInvalidArgument("title is required")
    }

    s.mu.Lock()
    defer s.mu.Unlock()

    s.nextID++
    todo := &Todo{
        ID:          fmt.Sprintf("%d", s.nextID),
        Title:       in.Title,
        Description: in.Description,
        CreatedAt:   time.Now(),
    }
    s.todos[todo.ID] = todo
    return todo, nil
}

func (s *Service) Get(ctx context.Context, in *GetInput) (*Todo, error) {
    s.mu.RLock()
    defer s.mu.RUnlock()

    todo, ok := s.todos[in.ID]
    if !ok {
        return nil, contract.ErrNotFound("todo not found")
    }
    return todo, nil
}

func (s *Service) List(ctx context.Context) (*ListOutput, error) {
    s.mu.RLock()
    defer s.mu.RUnlock()

    items := make([]*Todo, 0, len(s.todos))
    for _, t := range s.todos {
        items = append(items, t)
    }
    return &ListOutput{Items: items, Total: len(items)}, nil
}
```

```go theme={null}
// main.go
package main

import (
    "fmt"

    "github.com/go-mizu/mizu"
    contract "github.com/go-mizu/mizu/contract/v2"
    "github.com/go-mizu/mizu/contract/v2/transport/rest"

    "yourapp/todo"
)

func main() {
    impl := todo.NewService()

    svc := contract.Register[todo.API](impl,
        contract.WithName("Todo"),
        contract.WithDescription("A simple todo list API"),
        contract.WithDefaultResource("todos"),
    )

    app := mizu.New()

    // Mount REST API
    rest.Mount(app.Router, svc)

    // Serve OpenAPI specification
    specBytes, err := rest.OpenAPIDocument(svc)
    if err != nil {
        panic(err)
    }
    app.Get("/openapi.json", func(c *mizu.Ctx) error {
        c.Set("Content-Type", "application/json")
        return c.Send(specBytes)
    })

    // Serve Swagger UI
    app.Get("/docs", swaggerUIHandler)

    fmt.Println("Server: http://localhost:8080")
    fmt.Println("API:    http://localhost:8080/todos")
    fmt.Println("Spec:   http://localhost:8080/openapi.json")
    fmt.Println("Docs:   http://localhost:8080/docs")

    app.Listen(":8080")
}

func swaggerUIHandler(c *mizu.Ctx) error {
    return c.HTML(200, `<!DOCTYPE html>
<html><head>
    <title>Todo API Docs</title>
    <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
</head><body>
    <div id="swagger-ui"></div>
    <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
    <script>SwaggerUIBundle({url: "/openapi.json", dom_id: "#swagger-ui"});</script>
</body></html>`)
}
```

## Using the OpenAPI Spec

### Generate Client Code

Use tools like `openapi-generator` to create typed clients:

```bash theme={null}
# Install openapi-generator
npm install -g @openapitools/openapi-generator-cli

# Generate TypeScript client
openapi-generator-cli generate \
  -i http://localhost:8080/openapi.json \
  -g typescript-fetch \
  -o ./client

# Generate Python client
openapi-generator-cli generate \
  -i http://localhost:8080/openapi.json \
  -g python \
  -o ./python-client

# Generate Go client
openapi-generator-cli generate \
  -i http://localhost:8080/openapi.json \
  -g go \
  -o ./go-client
```

### Import into Postman

1. Open Postman
2. Click Import
3. Enter URL: `http://localhost:8080/openapi.json`
4. Your API is now ready to test!

### Validate with oasdiff

Check for breaking changes between versions:

```bash theme={null}
# Compare two versions of your spec
oasdiff diff old-api.json http://localhost:8080/openapi.json
```

## Service Metadata

Add service information that appears in the generated spec:

```go theme={null}
svc := contract.Register[todo.API](impl,
    contract.WithName("Todo"),
    contract.WithDescription("A simple todo list API"),
    contract.WithDefaultResource("todos"),
)
```

This produces:

```json theme={null}
{
  "openapi": "3.0.3",
  "info": {
    "title": "Todo",
    "description": "A simple todo list API",
    "version": "0.1.0"
  }
}
```

## Multiple Services

Generate specs for multiple services:

```go theme={null}
todoSvc := contract.Register[todo.API](todoImpl,
    contract.WithName("Todo"),
    contract.WithDefaultResource("todos"),
)
userSvc := contract.Register[user.API](userImpl,
    contract.WithName("User"),
    contract.WithDefaultResource("users"),
)

app := mizu.New()

// Mount REST endpoints
rest.Mount(app.Router, todoSvc)  // /todos
rest.Mount(app.Router, userSvc)  // /users

// Serve separate OpenAPI specs
todoSpec, _ := rest.OpenAPIDocument(todoSvc)
userSpec, _ := rest.OpenAPIDocument(userSvc)

app.Get("/api/todo/openapi.json", func(c *mizu.Ctx) error {
    c.Set("Content-Type", "application/json")
    return c.Send(todoSpec)
})

app.Get("/api/user/openapi.json", func(c *mizu.Ctx) error {
    c.Set("Content-Type", "application/json")
    return c.Send(userSpec)
})

app.Listen(":8080")
```

## Type Mappings

Contract converts Go types to OpenAPI schema types:

| Go Type        | OpenAPI Type       | Format                  |
| -------------- | ------------------ | ----------------------- |
| `string`       | `string`           | -                       |
| `bool`         | `boolean`          | -                       |
| `int`, `int32` | `integer`          | `int32`                 |
| `int64`        | `integer`          | `int64`                 |
| `float32`      | `number`           | `float`                 |
| `float64`      | `number`           | `double`                |
| `time.Time`    | `string`           | `date-time`             |
| `*T` (pointer) | `anyOf: [T, null]` | -                       |
| `[]T` (slice)  | `array`            | items: T                |
| `map[string]T` | `object`           | additionalProperties: T |

## Optional Fields

Use `omitempty` to mark fields as optional:

```go theme={null}
type CreateInput struct {
    Title       string `json:"title"`               // required
    Description string `json:"description,omitempty"` // optional
}
```

This produces:

```json theme={null}
{
  "type": "object",
  "properties": {
    "title": { "type": "string" },
    "description": { "type": "string" }
  },
  "required": ["title"]
}
```

## Path Parameters

Fields with the `path` tag are documented as path parameters:

```go theme={null}
type GetInput struct {
    ID string `json:"id" path:"id"`
}
```

This produces:

```json theme={null}
{
  "parameters": [
    {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": { "type": "string" }
    }
  ]
}
```

## Query Parameters

For GET requests, input fields not used in the path become query parameters:

```go theme={null}
type ListInput struct {
    Status string `json:"status" query:"status"`
    Limit  int    `json:"limit" query:"limit"`
}
```

This produces:

```json theme={null}
{
  "parameters": [
    {
      "name": "status",
      "in": "query",
      "required": true,
      "schema": { "type": "string" }
    },
    {
      "name": "limit",
      "in": "query",
      "required": true,
      "schema": { "type": "integer", "format": "int32" }
    }
  ]
}
```

## Common Questions

### How do I add descriptions to endpoints?

Method descriptions come from the `contract.WithDescription` option at the method level (not yet implemented). For now, the operation ID includes the method name.

### What OpenAPI version is generated?

OpenAPI 3.0.3, a widely supported version.

### Can I customize the generated spec?

The current generator produces a standard spec. For extensive customization:

1. Generate the spec with `rest.OpenAPIDocument(svc)`
2. Parse and modify it programmatically
3. Serve the modified version

### Does it support authentication schemes?

Yes, when using `WithDefaults` with auth settings:

```go theme={null}
svc := contract.Register[todo.API](impl,
    contract.WithDefaults(contract.Defaults{
        Auth: "bearer",
    }),
)
```

This adds bearer token security to the spec.

### Can I add custom tags or extensions?

Not directly. The generator creates a minimal, standards-compliant spec. For advanced customization, modify the generated spec programmatically.

## What's Next?

* **[REST Transport](/contract/rest)** - The endpoints documented by OpenAPI
* **[Type System](/contract/types)** - How Go types become JSON schemas
* **[Transports Overview](/contract/transports-overview)** - Compare all transports
