# API Versioning

The [versioning](https://github.com/kataras/iris/tree/main/versioning) subpackage provides [semver](https://semver.org/) versioning for your APIs.

All best practice conventions are implemented, including the suggestions written in the [api-guidelines document](https://github.com/byrondover/api-guidelines/blob/master/Guidelines.md#versioning).

* The version is retrieved from the `"Accept"` and/or `"Accept-Version"` request header(s).
* If a version matched, the server responds with a custom `"X-Api-Version"` header with the normalized version inside.
* If a version was not found (missing version header or unimplemented version) the server responds with status code of 501 and plain text of "version not found" by default.
* If a specific version of the resource is deprecated by the server, the client receives the custom `"X-Api-Warn"`, `"X-Api-Deprecation-Date"` and `"X-Api-Deprecation-Info"` headers, the request is handled as expected.

## Introduction

Internally, the version validation is done by the regex-free and fast [github.com/blang/semver/v4](https://github.com/blang/semver/v4) third-party package.

Valid version ranges are:

* "<1.0.0"
* "<=1.0.0"
* ">1.0.0"
* ">=1.0.0"
* "1.0.0", "=1.0.0", "==1.0.0"
* "!1.0.0", "!=1.0.0"

A Range can consist of multiple ranges separated by space: Ranges can be linked by logical AND:

* ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0"
* ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2

Ranges can also be linked by logical OR:

* "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"

AND has a higher precedence than OR. It's not possible to use brackets.

Ranges can be combined by both AND and OR

* `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`.

## Getting Started

Import the `versioning` package.

```go
import (
    // [...]

    "github.com/kataras/iris/v12"
    "github.com/kataras/iris/v12/versioning"
)
```

Using the `versioning.NewGroup(version string) *versioning.Group` function you can create a group of routes to register the application's routes based on version requested by clients. The `versioning.Group` completes the `iris.Party` interface.

Example Code:

```go
package main

import (
    "github.com/kataras/iris/v12"
    "github.com/kataras/iris/v12/versioning"
)

func main() {
    app := iris.New()

    app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
        ctx.WriteString(`Root not found handler.
        This will be applied everywhere except the /api/* requests.`)
    })

    api := app.Party("/api")
    // Optional, set version aliases (literal strings).
    // We use `UseRouter` instead of `Use`
    // to handle HTTP errors per version, but it's up to you.
    api.UseRouter(versioning.Aliases(versioning.AliasMap{
        // If no version provided by the client, default it to the "1.0.0".
        versioning.Empty: "1.0.0",
        // If a "latest" version is provided by the client,
        // set the version to be compared to "3.0.0".
        "latest": "3.0.0",
    }))

    // |----------------|
    // | The fun begins |
    // |----------------|

    // Create a new Group, which is a compatible Party,
    // based on version constraints.
    v1 := versioning.NewGroup(api, ">=1.0.0 <2.0.0")

    // Optionally, set custom view engine and path
    // for templates based on the version.
    v1.RegisterView(iris.HTML("./v1", ".html"))

    // Optionally, set custom error handler(s) based on the version.
    // Keep in mind that if you do this, you will
    // have to register error handlers
    // for the rest of the parties as well.
    v1.OnErrorCode(iris.StatusNotFound, testError("v1"))

    // Register resources based on the version.
    v1.Get("/", testHandler("v1"))
    v1.Get("/render", testView)

    // Do the same for version 2 and version 3,
    // for the sake of the example.
    v2 := versioning.NewGroup(api, ">=2.0.0 <3.0.0")
    v2.RegisterView(iris.HTML("./v2", ".html"))
    v2.OnErrorCode(iris.StatusNotFound, testError("v2"))
    v2.Get("/", testHandler("v2"))
    v2.Get("/render", testView)

    v3 := versioning.NewGroup(api, ">=3.0.0 <4.0.0")
    v3.RegisterView(iris.HTML("./v3", ".html"))
    v3.OnErrorCode(iris.StatusNotFound, testError("v3"))
    v3.Get("/", testHandler("v3"))
    v3.Get("/render", testView)

    app.Listen(":8080")
}

func testHandler(v string) iris.Handler {
    return func(ctx iris.Context) {
        ctx.JSON(iris.Map{
            "version": v,
            "message": "Hello, world!",
        })
    }
}

func testError(v string) iris.Handler {
    return func(ctx iris.Context) {
        ctx.Writef("not found: %s", v)
    }
}

func testView(ctx iris.Context) {
    ctx.View("index.html")
}
```

## Version Aliases

Optional, set version aliases (literal strings).

```go
api.UseRouter(versioning.Aliases(versioning.AliasMap{
    // If no version provided by the client, default it to the "1.0.0".
    versioning.Empty: "1.0.0",
    // If a "latest" version is provided by the client,
    // set the version to be compared to "3.0.0".
    "latest": "3.0.0",
}))
```

We use `UseRouter` instead of `Use` to handle HTTP errors per version, but it's up to you.

## Get Current Version

The version is extracted through the `versioning.GetVersion` function. You can use it anywhere, even if you don't use the versioning feature. By default `GetVersion` will try to read from:

* `Accept` header, e.g. `Accept: "application/json; version=1.0.0"`
* `Accept-Version` header, e.g. `Accept-Version: "1.0.0"`

You can customize it by **setting a version** based on the request context:

```go
api.Use(func(ctx *context.Context) {
    if version := ctx.URLParam("version"); version != "" {
        versioning.SetVersion(ctx, version)
    }

    ctx.Next()
})
```

Or by using the `FromQuery(version, default)` helper:

```go
api.Use(versioning.FromQuery("version", "1.0.0"))
```

### Deprecation

To mark an API version as deprecated use the `Deprecated` method.

```go
v1.Deprecated(versioning.DefaultDeprecationOptions)
```

This will make the route handlers to send some extra headers to the client.

* `"X-API-Warn": options.WarnMessage`
* `"X-API-Deprecation-Date": context.FormatTime(ctx, options.DeprecationDate))`
* `"X-API-Deprecation-Info": options.DeprecationInfo`


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://iris-go.gitbook.io/iris/routing/api-versioning.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
