diff --git a/API-versioning.md b/API-versioning.md new file mode 100644 index 0000000..9aad46d --- /dev/null +++ b/API-versioning.md @@ -0,0 +1,140 @@ +The [versioning](https://github.com/kataras/iris/tree/master/versioning) subpackage provides [semver](https://semver.org/) versioning for your APIs. It implements all the suggestions written at [api-guidelines](https://github.com/byrondover/api-guidelines/blob/master/Guidelines.md#versioning) and more. + +The version comparison is done by the [go-version](https://github.com/hashicorp/go-version) package. It supports matching over patterns like `">= 1.0, < 3"` and etc. + +## Features + +- per route version matching, a normal iris handler with "switch" cases via Map for version => handler +- per group versioned routes and deprecation API +- version matching like ">= 1.0, < 2.0" or just "2.0.1" and etc. +- version not found handler (can be customized by simply adding the versioning.NotFound: customNotMatchVersionHandler on the Map) +- version is retrieved from the "Accept" and "Accept-Version" headers (can be customized via middleware) +- respond with "X-API-Version" header, if version found. +- deprecation options with customizable "X-API-Warn", "X-API-Deprecation-Date", "X-API-Deprecation-Info" headers via `Deprecated` wrapper. + +## Get version + +Current request version is retrieved by `versioning.GetVersion(ctx)`. + +By default the `GetVersion` will try to read from: +- `Accept` header, i.e `Accept: "application/json; version=1.0"` +- `Accept-Version` header, i.e `Accept-Version: "1.0"` + +You can also set a custom version for a handler via a middleware by using the context's store values. +For example: + +```go +func(ctx iris.Context) { + ctx.Values().Set(versioning.Key, ctx.URLParamDefault("version", "1.0")) + ctx.Next() +} +``` + +## Match version to handler + +The `versioning.NewMatcher(versioning.Map) iris.Handler` creates a single handler which decides what handler need to be executed based on the requested version. + +```go +app := iris.New() + +// middleware for all versions. +myMiddleware := func(ctx iris.Context) { + // [...] + ctx.Next() +} + +myCustomNotVersionFound := func(ctx iris.Context) { + ctx.StatusCode(404) + ctx.Writef("%s version not found", versioning.GetVersion(ctx)) +} + +userAPI := app.Party("/api/user") +userAPI.Get("/", myMiddleware, versioning.NewMatcher(versioning.Map{ + "1.0": sendHandler(v10Response), + ">= 2, < 3": sendHandler(v2Response), + versioning.NotFound: myCustomNotVersionFound, +})) +``` + +### Deprecation + +Using the `versioning.Deprecated(handler iris.Handler, options versioning.DeprecationOptions) iris.Handler` function you can mark a specific handler version as deprecated. + + +```go +v10Handler := versioning.Deprecated(sendHandler(v10Response), versioning.DeprecationOptions{ + // if empty defaults to: "WARNING! You are using a deprecated version of this API." + WarnMessage string + DeprecationDate time.Time + DeprecationInfo string +}) + +userAPI.Get("/", versioning.NewMatcher(versioning.Map{ + "1.0": v10Handler, + // [...] +})) +``` + +This will make the handler to send these headers to the client: + +- `"X-API-Warn": options.WarnMessage` +- `"X-API-Deprecation-Date": context.FormatTime(ctx, options.DeprecationDate))` +- `"X-API-Deprecation-Info": options.DeprecationInfo` + +> versioning.DefaultDeprecationOptions can be passed instead if you don't care about Date and Info. + +## Grouping routes by version + +Grouping routes by version is possible as well. + +Using the `versioning.NewGroup(version string) *versioning.Group` function you can create a group to register your versioned routes. +The `versioning.RegisterGroups(r iris.Party, versionNotFoundHandler iris.Handler, groups ...*versioning.Group)` must be called in the end in order to register the routes to a specific `Party`. + +```go +app := iris.New() + +userAPI := app.Party("/api/user") +// [... static serving, middlewares and etc goes here]. + +userAPIV10 := versioning.NewGroup("1.0") +userAPIV10.Get("/", sendHandler(v10Response)) + +userAPIV2 := versioning.NewGroup(">= 2, < 3") +userAPIV2.Get("/", sendHandler(v2Response)) +userAPIV2.Post("/", sendHandler(v2Response)) +userAPIV2.Put("/other", sendHandler(v2Response)) + +versioning.RegisterGroups(userAPI, versioning.NotFoundHandler, userAPIV10, userAPIV2) +``` + +> A middleware can be registered to the actual `iris.Party` only, using the methods we learnt above, i.e by using the `versioning.Match` in order to detect what code/handler you want to be executed when "x" or no version is requested. + +### Deprecation for Group + +Just call the `Deprecated(versioning.DeprecationOptions)` on the group you want to notify your API consumers that this specific version is deprecated. + +```go +userAPIV10 := versioning.NewGroup("1.0").Deprecated(versioning.DefaultDeprecationOptions) +``` + +## Compare version manually from inside your handlers + +```go +// reports if the "version" is matching to the "is". +// the "is" can be a constraint like ">= 1, < 3". +If(version string, is string) bool +``` + +```go +// same as `If` but expects a Context to read the requested version. +Match(ctx iris.Context, expectedVersion string) bool +``` + +```go +app.Get("/api/user", func(ctx iris.Context) { + if versioning.Match(ctx, ">= 2.2.3") { + // [logic for >= 2.2.3 version of your handler goes here] + return + } +}) +``` diff --git a/Home.md b/Home.md index cc1196b..d4c3213 100644 --- a/Home.md +++ b/Home.md @@ -17,6 +17,7 @@ This wiki is the main source of documentation for **developers** working with (o * [[Subdomains|Routing-subdomains]] * [[Wrap the Router|Routing-wrap-the-router]] * [[Dependency Injection|Routing-dependency-injection]] +* [[API Versioning]] * [[Cookies]] > This new wiki is a `Work In Progress` in an effort to centralize the documentation and concepts explanation into a single place. diff --git a/_Sidebar.md b/_Sidebar.md index 8f3cb08..3e3ec61 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -12,4 +12,5 @@ * [[Subdomains|Routing-subdomains]] * [[Wrap the Router|Routing-wrap-the-router]] * [[Dependency Injection|Routing-dependency-injection]] +* [[API Versioning]] * [[Cookies]]