> ## 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.

# Version

> API versioning middleware for managing multiple API versions.

## Overview

The `version` middleware provides API versioning support through headers, query parameters, or URL path prefixes. It helps manage multiple API versions and handles deprecation warnings.

## Installation

```go theme={null}
import "github.com/go-mizu/mizu/middlewares/version"
```

## Quick Start

```go theme={null}
app := mizu.New()

app.Use(version.New(version.Options{
    DefaultVersion: "v1",
}))

app.Get("/users", func(c *mizu.Ctx) error {
    v := version.Get(c)
    // Handle based on version
    return c.JSON(200, users)
})
```

## Configuration

| Option           | Type                            | Default            | Description                        |
| ---------------- | ------------------------------- | ------------------ | ---------------------------------- |
| `DefaultVersion` | `string`                        | -                  | Default version when not specified |
| `Header`         | `string`                        | `"Accept-Version"` | Header name for version            |
| `QueryParam`     | `string`                        | `"version"`        | Query parameter name               |
| `PathPrefix`     | `bool`                          | `false`            | Extract version from URL path      |
| `Supported`      | `[]string`                      | -                  | List of supported versions         |
| `Deprecated`     | `[]string`                      | -                  | List of deprecated versions        |
| `ErrorHandler`   | `func(*mizu.Ctx, string) error` | -                  | Custom error handler               |

## Examples

### Version from Header

```go theme={null}
app.Use(version.New(version.Options{
    DefaultVersion: "v1",
    Header:         "Accept-Version",
}))

app.Get("/api/users", func(c *mizu.Ctx) error {
    switch version.Get(c) {
    case "v2":
        return c.JSON(200, usersV2)
    default:
        return c.JSON(200, usersV1)
    }
})
```

Client request:

```
GET /api/users HTTP/1.1
Accept-Version: v2
```

### Version from Query Parameter

```go theme={null}
app.Use(version.New(version.Options{
    DefaultVersion: "v1",
    QueryParam:     "api_version",
}))
```

Request: `GET /users?api_version=v2`

### Version from URL Path

```go theme={null}
app.Use(version.New(version.Options{
    PathPrefix: true,
}))

// GET /v1/users → version "v1"
// GET /v2/users → version "v2"
```

### Supported Versions Only

```go theme={null}
app.Use(version.New(version.Options{
    DefaultVersion: "v2",
    Supported:      []string{"v1", "v2", "v3"},
    ErrorHandler: func(c *mizu.Ctx, v string) error {
        return c.JSON(400, map[string]string{
            "error": "Unsupported API version: " + v,
            "supported": "v1, v2, v3",
        })
    },
}))
```

### Deprecation Warnings

```go theme={null}
app.Use(version.New(version.Options{
    DefaultVersion: "v3",
    Supported:      []string{"v1", "v2", "v3"},
    Deprecated:     []string{"v1"},
}))

// Requests with v1 will include:
// Deprecation: true
// Sunset: See documentation for migration guide
```

### Using Helper Functions

```go theme={null}
// Version from header only
app.Use(version.FromHeader("X-API-Version"))

// Version from path only
app.Use(version.FromPath())

// Version from query only
app.Use(version.FromQuery("v"))
```

### Version-Specific Routes

```go theme={null}
app.Use(version.New(version.Options{
    PathPrefix: true,
}))

app.Get("/v1/users", usersV1Handler)
app.Get("/v2/users", usersV2Handler)
app.Get("/v3/users", usersV3Handler)
```

### Content Negotiation

```go theme={null}
app.Get("/users", func(c *mizu.Ctx) error {
    v := version.Get(c)

    switch v {
    case "v3":
        return c.JSON(200, map[string]any{
            "data":  users,
            "meta":  metadata,
            "links": links,
        })
    case "v2":
        return c.JSON(200, map[string]any{
            "users": users,
            "total": len(users),
        })
    default:
        return c.JSON(200, users)
    }
})
```

### Feature Flags by Version

```go theme={null}
app.Get("/users", func(c *mizu.Ctx) error {
    v := version.Get(c)

    users := getUsers()

    // New field only in v2+
    if v >= "v2" {
        for i := range users {
            users[i].Avatar = getAvatar(users[i].ID)
        }
    }

    return c.JSON(200, users)
})
```

## API Reference

```go theme={null}
func New(opts Options) mizu.Middleware
func FromHeader(header string) mizu.Middleware
func FromPath() mizu.Middleware
func FromQuery(param string) mizu.Middleware
func GetVersion(c *mizu.Ctx) string
func Get(c *mizu.Ctx) string  // Alias for GetVersion
```

## Version Detection Order

When multiple sources are configured:

1. Header (e.g., `Accept-Version: v2`)
2. Query parameter (e.g., `?version=v2`)
3. Path prefix (e.g., `/v2/users`)
4. Default version

## Response Headers

For deprecated versions:

```
Deprecation: true
Sunset: See documentation for migration guide
```

## Technical Details

### Architecture

The version middleware uses a context-based approach to store and retrieve version information:

1. **Version Detection**: The middleware examines multiple sources in a specific priority order
2. **Context Storage**: Detected version is stored in the request context using a private context key
3. **Validation**: Optional validation against supported/deprecated version lists
4. **Header Injection**: Automatic deprecation headers for deprecated versions

### Implementation Details

**Version Detection Priority**:

1. HTTP Header (configurable, default: `Accept-Version`)
2. Query Parameter (configurable, default: `version`)
3. Path Prefix (optional, pattern: `/v{number}/...`)
4. Default Version (fallback)

**Version String Validation**:

* Pattern: `v` or `V` followed by digits and optional dots
* Examples: `v1`, `v2`, `V1`, `v1.0`, `v1.2.3`
* Invalid: `api`, empty string, `v`, `va`

**Context Management**:

* Uses a private `contextKey{}` struct to prevent collisions
* Version stored as string in request context
* Retrieved via `GetVersion(c)` or `Get(c)` helper functions

**Performance Optimizations**:

* Supported and deprecated versions stored in maps for O(1) lookup
* Single pass through version sources with early exit
* No regex matching for version strings (character-by-character validation)

### Response Headers

For deprecated versions, the middleware automatically adds:

* `Deprecation: true` - Indicates the version is deprecated
* `Sunset: See documentation for migration guide` - Migration information

## Best Practices

* Always set a default version
* Document supported versions
* Add deprecation warnings before removal
* Use semantic versioning (v1, v2, v3)
* Maintain backward compatibility within major versions
* Provide migration guides for deprecated versions

## Testing

The middleware includes comprehensive test coverage for all features:

| Test Case                               | Description                        | Expected Behavior                         |
| --------------------------------------- | ---------------------------------- | ----------------------------------------- |
| `TestNew/from header`                   | Version from Accept-Version header | Extracts version "v2" from header         |
| `TestNew/from query`                    | Version from query parameter       | Extracts version "v3" from ?version=v3    |
| `TestNew/default version`               | No version specified               | Uses default version "v1"                 |
| `TestNew_PathPrefix/v1`                 | Version from path prefix /v1       | Extracts version "v1" from URL path       |
| `TestNew_PathPrefix/v2`                 | Version from path prefix /v2       | Extracts version "v2" from URL path       |
| `TestNew_Supported/supported version`   | Request with supported version     | Returns 200 OK                            |
| `TestNew_Supported/unsupported version` | Request with unsupported version   | Returns 400 Bad Request                   |
| `TestNew_Deprecated`                    | Request with deprecated version    | Sets Deprecation header to "true"         |
| `TestFromHeader`                        | Custom header name (X-API-Version) | Extracts version "2.0" from custom header |
| `TestFromPath`                          | Path with semantic version (v1.2)  | Extracts version "v1.2" from path         |
| `TestFromQuery`                         | Custom query param (api\_version)  | Extracts version "3.0" from custom param  |
| `TestGet`                               | GetVersion vs Get function         | Both functions return same value          |
| `TestIsVersionString/v1`                | Valid version string "v1"          | Returns true                              |
| `TestIsVersionString/v2`                | Valid version string "v2"          | Returns true                              |
| `TestIsVersionString/V1`                | Valid uppercase "V1"               | Returns true                              |
| `TestIsVersionString/v1.0`              | Valid with dot "v1.0"              | Returns true                              |
| `TestIsVersionString/v1.2.3`            | Valid semantic "v1.2.3"            | Returns true                              |
| `TestIsVersionString/api`               | Invalid string "api"               | Returns false                             |
| `TestIsVersionString/empty`             | Empty string                       | Returns false                             |
| `TestIsVersionString/v`                 | Just "v" character                 | Returns false                             |
| `TestIsVersionString/va`                | Invalid "va"                       | Returns false                             |

## Related Middlewares

* [maintenance](/middlewares/maintenance) - Maintenance mode
* [header](/middlewares/header) - Custom headers
