Skip to main content

API Version

Manage the life cycle of the API, handling multiple versions.


Introduction

Traefik Hub offers the option to define distinct configurations for each version of your APIs.

API versioning is the practice of creating and managing multiple versions of an API to handle changes, updates, and improvements over time. With Traefik Hub and API versioning, you can maintain backward compatibility and support existing clients.

When declaring a new version, you must have an object for the root version, and an object for each version, defining the rules to access not only the service that implements the API, but also the different routes that will match the given version.


Versioning Strategies

There are various strategies for implementing API versioning, such as URI path versioning, query versioning, header versioning, or using custom media types.

Each API version carries its Open API Specification file (when applicable) and can depend on the same or different services.

In Traefik Hub, you can use as many strategies at the same time for your APIs.

StrategyDescription
URI PathThe version number depends on the path of the API.
Query ParameterThe version number depends on a query parameter.
HeaderThe version number depends on a header within the query.
Media TypeThe version number depends on the value of the header Media Type in the query.

Using the CRDs to Manage APIs

General

When defining multiple versions for an API, you will create a root API Object, then an APIVersion object for each subsequent version.

FieldDescriptionRequired
apiNameName of the API.Yes
releaseRelease number following SemVer (Semantic Versioning). release isn't tied to the defined version matcher(s) of your API.Yes
titleHuman friendly name of the release which will be displayed in the dashboard overview of an API.No
service.nameName for the Service, we recommend using kebab-case, for example, my-app.Yes
service.port.numberThe Service port where the API is reachable.Yes
service.openApiSpec.urlURL, pointing to a valid OpenAPI file. The file can be external (hosted anywhere). The Portal must be able to retrieve it without any kind of authentication.No
service.openApiSpec.pathYAML and JSON are supported file formats for OpenAPI files.No
service.openApiSpec.operationSets.nameThe name of the operationSet used for operation filtering.No
service.openApiSpec.operationSets.matchersOne of the following path settings: path, pathPrefix or pathRegex.
You can also use methods or a combination of a path setting and methods.
No
service.openApiSpec.operationSets.matchers.pathPrefixDefines the path prefix the selected spec operations have to start with.
For example, /customers, /customers/stats.
No
service.openApiSpec.operationSets.matchers.pathRegexDefines the path regex the selected spec operations have to match.
For example, /customers/.*/address.
Regular expressions should follow the Golang style.
No
service.openApiSpec.operationSets.matchers.methodsDefines a set of HTTP methods of the spec operation to select.
For example, ["GET"] or ["GET", "POST"].
No
stripPathPrefixRemove prefixes from the URL path. For example, if the pathPrefix is set to /v1.0, /v1.0 will be removed from the path when passing the request to the service.No
routes[n].pathPrefixThe URL prefix identifying the API version. It is possible to use multiple pathPrefix and to use it together with headers and queryParams.
See the advanced examples below.
No
routes[n].headersSet the version number using custom headers in requests and responses. It is possible to use multiple headers and to use it together with pathPrefix and queryParams.
See the advanced examples below for more info.
No
routes[n].queryParamsUse query parameter for versioning. It is possible to use multiple queryParams, for example, version:v2.0.0 and lang:fr. queryParams can be used together with pathPrefix and headers.
See the examples below.
No
cors.allowCredentialsThe header tells browsers whether to expose the response to the frontend JavaScript code when the request's credentials mode (Request.credentials) is include.
Documentation on MDN.
No
cors.allowOriginListThe header indicates whether the response can be shared with requesting code from the given origin.
Documentation on MDN.
No
cors.allowOriginListRegexIndicates whether a resource can be shared by returning different values. It allows all origins that contain any match of a regular expression in the Access-Control-Allow-Origin list.
Documentation on MDN.
No
cors.allowHeadersUsed in response to a preflight request which includes the Access-Control-Request-Headers to indicate which HTTP headers can be used during the actual request.
Documentation on MDN.
No
cors.ExposeHeadersIndicates which headers are safe to expose to the API of a CORS API specification.
Documentation on MDN.
No
cors.allowMethodsHTTP defines a set of request methods to indicate the action to be performed for a given resource.
Documentation on MDN.
No
cors.MaxAgeThe header indicates how long the results of a preflight request (that is, the information contained in the Access-Control-Allow-Methods and Access-Control-Allow-Headers headers) can be cached. Documentation on MDNNo
headers.request.setAdd custom request headers to an API.No
headers.request.deleteRemove a header from an API request.No
headers.response.setAdd custom request headers to an API.No
headers.response.deleteRemove a header from an API response.No
info

If you don't configure routes[n].pathPrefix, routes[n].headers or routes[n].queryParams for versioning, the version will act as a catch-all.

URI Path Examples

In the URI path strategy, the version number in the path of the URI.

Defines the common characteristics of the API along with the current version
apiVersion: hub.traefik.io/v1alpha1
kind: API
metadata:
name: flight-api
namespace: apps
labels:
area: customers
module: crm
spec:
pathPrefix: "/flight"
currentVersion: flight-api-v2

Query Parameter Examples

This type of versioning adds one (or multiple) query parameter to the request that indicates the version.

Defines the common characteristics of the API along with the current version
apiVersion: hub.traefik.io/v1alpha1
kind: API
metadata:
name: flight-api
namespace: apps
labels:
area: customers
module: crm
spec:
pathPrefix: "/flight"
currentVersion: flight-api-v2

Header Examples

In this method, you use a custom header in the request. This leaves the URI of your resources unchanged.

Defines the common characteristics of the API along with the current version
apiVersion: hub.traefik.io/v1alpha1
kind: API
metadata:
name: flight-api
namespace: apps
labels:
area: customers
module: crm
spec:
pathPrefix: "/flight"
currentVersion: flight-api-v2

Media Type Examples

Media type versioning, also known as "content negotiation versioning", is the practice of specifying the protocol version via the Content-Type HTTP header.

Defines the common characteristics of the API along with the current version
apiVersion: hub.traefik.io/v1alpha1
kind: API
metadata:
name: flight-api
namespace: apps
labels:
area: customers
module: crm
spec:
pathPrefix: "/flight"
currentVersion: flight-api-v2

CORS

You can define CORS for each APIVersion.

Defining CORS for Version 2 of the API
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flights-api-v2
namespace: apps
spec:
apiName: flights-api
release: v2.0.0
title: "Flights API"
routes:
- pathPrefix: "/v2.0.0"
stripPathPrefix: true
service:
name: flights-svc-v2
port:
number: 8080
openApiSpec:
path: /api/v2/openapi.json
cors:
allowCredentials: true
allowOriginList:
- "*"
allowHeaders:
- "Accept"
- "Accept-Language"
- "Content-Language"
- "Content-Type"
- "Authorization"
- "X-TraefikLabs-User"
allowMethods:
- "GET"
- "HEAD"
- "POST"
- "PUT"

Manipulating Headers

API Version with Header Manipulation
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: my-flights-api-v2
namespace: apps
spec:
apiName: my-versioned-flights-api
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
routes:
- pathPrefix: "/v2.0.0"
stripPathPrefix: true
service:
name: flights-svc-v2
port:
number: 8080
openApiSpec:
path: /api/v2/openapi.json
headers:
request:
set:
"X-Request-Header": "Custom request header"
"X-Username": "Somebody"
delete:
- "Unnecessary-Request-Header"
response:
set:
"X-Response-Header": "Custom response header"
"X-API-Server": "Traefik Hub"
delete:
- "Secret-Response-Header"

Combining Routes

It is possible to define multiple routes for API versions and Services.

  • An API can have a multiple version routes
  • An API can use multiple constraints on a route
  • It is possible to use different route types per API version

If you want to make an API available on different routes, for example versioning an API using pathPrefix OR header define one block per route.

Versioning by Path OR Header
routes:
# The API will available at two routes.
# 1. At the URI version path https://api.example.com/flights/v2.0.0
- pathPrefix: "/v2.0.0"
# 2. At the URI https://api.example.com/flights using a `header`.
- headers:
Content: "application/vnd.example.v2+json"

If you want to use a constraint that must match, for example, a pathPrefix AND a header, define the array item as a map.

Both the Path and Headers must match
routes:
# The API will be available at one route and both constraints must match.
# Example: curl -H "Content: application/vnd.example.v2+json" https://api.example.com/flights/v2.0.0
- pathPrefix: "/v2.0.0"
headers:
Content: "application/vnd.example.v2+json"

Operation Filtering

info

If you don't version your APIs, you only need to configure operation filtering in the API CRD.

Operation filtering is a way to restrict user groups to a specific set of OpenAPI operations.

Imagine you have an API with the following use case:

  • Only members of the admin group are allowed to delete customers.
  • Only members of the admin group are allowed to retrieve customer statists
  • Members of the support group are allowed to retrieve a list with all customers

Configuring operation filtering is done in multiple steps.
You have to configure operationSets on your APIs, and reference these in your APIAccess.

OperationSets

An operationSet consists of the following:

  • A name, which will be referenced in the APIAccess.
  • A matcher to select the API operation(s) which will be part of the OperationSet.
Path
# Example of versioning an API using `pathPrefix` for URI path.
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: my-flights-api-v2
namespace: apps
spec:
# The API this version is referencing (assumed to be in the same namespace).
apiName: flight-api
# SemVer of the release
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
routes:
# The API will be available on one route using URI path for versioning.
# Example: curl https://api.example.com/flights/v2.0.0
- pathPrefix: "/v2.0.0"
# The path prefix of the route will be removed and not forwarded with the request.
stripPathPrefix: true
service:
name: flights-svc-v2
port:
number: 8080
openApiSpec:
path: /api/v2/openapi.json
operationSets:
- name: flight-operations
matchers:
- path: /flights
note
  • Only one of the following path settings is allowed: path, pathPrefix, or pathRegex.
  • You can use the methods option without using a path setting (path, pathPrefix, or pathRegex).
  • It is possible to combine a path setting with the methods option.
  • The pathRegex must match both the OpenAPI specification and the request.
    For example, the request to /flights/:id shouldn't be expressed by /flights/[0-9]+ but by /flights/.+ to satisfy both /flights/{flightID} in the OAS specification and /flights/2 in the actual request made by the user.
Be aware of the subtle configuration difference

Please be aware that the difference between configuring one matcher with multiple options or configuring multiple matchers is subtle.
The examples below show the difference in the syntax.

One matcher with two options, both of the options must align
operationSets:
- name: read-pets
matchers:
- pathPrefix: /pets
methods:
- GET

Without a Defined HTTP Method

The following example shows an API CRD using an operation matcher with pathPrefix, without any HTTP method defined.
This would allow all HTTP methods matching the /flights path prefix.
For example, GET /flights, GET /flights/{id}, POST /flights, PUT /flights/{id}.

Without a Defined HTTP Method
# Example of versioning an API using `pathPrefix` for URI path.
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: my-flights-api-v2
namespace: apps
spec:
# The API this version is referencing (assumed to be in the same namespace).
apiName: flight-api
# SemVer of the release
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
routes:
# The API will be available on one route using URI path for versioning.
# Example: curl https://api.example.com/flights/v2.0.0
- pathPrefix: "/v2.0.0"
# The path prefix of the route will be removed and not forwarded with the request.
stripPathPrefix: true
service:
name: flights-svc-v2
port:
number: 8080
openApiSpec:
path: /api/v2/openapi.json
operationSets:
- name: flight-operations
matchers:
- pathPrefix: /flights

Every Matcher Option Must Align

The next example shows an API CRD using one matcher with two options.
One doing path prefix filtering and one doing method filtering.

Both options must be fulfilled.

Setting two options, one with pathPrefix and the other with methods (act as a logical AND).

This allows GET for all operations matching the /flights path prefix.
For example, GET /flights, GET /flights/{id}, GET /flights/findByStatus.

Every Matcher Option Must Align
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: my-flights-api-v2
namespace: apps
spec:
# The API this version is referencing (assumed to be in the same namespace).
apiName: flight-api
# SemVer of the release
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
routes:
# The API will be available on one route using URI path for versioning.
# Example: curl https://api.example.com/flights/v2.0.0
- pathPrefix: "/v2.0.0"
# The path prefix of the route will be removed and not forwarded with the request.
stripPathPrefix: true
service:
name: flights-svc-v2
port:
number: 8080
openApiSpec:
path: /api/v2/openapi.json
operationSets:
- name: read-flights
matchers:
- pathPrefix: /flights
methods:
- GET

One of the Two Matchers Must Match

The next example shows an API CRD using a combination of two matchers.
One doing path prefix filtering and one doing method filtering.

One of the matchers must fulfil.

Setting two matchers, one with pathPrefix and the other with methods (act as a logical OR).

This allows all operations matching the /flights path prefix.
This allows GET operations on all endpoints of the API For example, POST /flights, GET /airports/{id}.

One of the Two Matchers Must Match
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: my-flights-api-v2
namespace: apps
spec:
# The API this version is referencing (assumed to be in the same namespace).
apiName: flight-api
# SemVer of the release
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
routes:
# The API will be available on one route using URI path for versioning.
# Example: curl https://api.example.com/flights/v2.0.0
- pathPrefix: "/v2.0.0"
# The path prefix of the route will be removed and not forwarded with the request.
stripPathPrefix: true
service:
name: flights-svc-v2
port:
number: 8080
openApiSpec:
path: /api/v2/openapi.json
operationSets:
- name: read-flights
matchers:
- pathPrefix: /flights
- methods:
- GET

Allow Different Operations for Different Groups

The next example shows an API CRD using four operationSets to allow different operations for different groups.
path is used to set the exact path for allowed HTTP methods for the different operationSets.

These operationSets are then used by operationFilter in the APIAccess CRD to configure permissions.

Allow Different Operations for Different Groups
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: my-flights-api-v2
namespace: apps
spec:
# The API this version is referencing (assumed to be in the same namespace).
apiName: flight-api
# SemVer of the release
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
routes:
# The API will be available on one route using URI path for versioning.
# Example: curl https://api.example.com/flights/v2.0.0
- pathPrefix: "/v2.0.0"
# The path prefix of the route will be removed and not forwarded with the request.
stripPathPrefix: true
service:
name: flights-svc-v2
port:
number: 8080
openApiSpec:
path: /api/v2/openapi.json
operationSets:
- name: get-flights
matchers:
methods: ["GET"]
path: "/flights"
- name: delete-flights
matchers:
methods: ["DELETE"]
path: "/flights"
- name: cru-resources
matchers:
methods: ["GET","POST","PUT", "PATCH"]
- name: admin-operations
matchers:
pathPrefix: "/admin"