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.
Strategy | Description |
---|---|
URI Path | The version number depends on the path of the API. |
Query Parameter | The version number depends on a query parameter. |
Header | The version number depends on a header within the query. |
Media Type | The 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.
Field | Description | Required |
---|---|---|
apiName | Name of the API. | Yes |
release | Release number following SemVer (Semantic Versioning). release isn't tied to the defined version matcher(s) of your API. | Yes |
title | Human friendly name of the release which will be displayed in the dashboard overview of an API. | No |
service.name | Name for the Service, we recommend using kebab-case, for example, my-app. | Yes |
service.port.number | The Service port where the API is reachable. | Yes |
service.openApiSpec.url | URL, 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.path | YAML and JSON are supported file formats for OpenAPI files. | No |
service.openApiSpec.operationSets.name | The name of the operationSet used for operation filtering. | No |
service.openApiSpec.operationSets.matchers | One 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.pathPrefix | Defines the path prefix the selected spec operations have to start with. For example, /customers, /customers/stats. | No |
service.openApiSpec.operationSets.matchers.pathRegex | Defines 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.methods | Defines a set of HTTP methods of the spec operation to select. For example, ["GET"] or ["GET", "POST"]. | No |
stripPathPrefix | Remove 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].pathPrefix | The 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].headers | Set 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].queryParams | Use 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.allowCredentials | The 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.allowOriginList | The header indicates whether the response can be shared with requesting code from the given origin . Documentation on MDN. | No |
cors.allowOriginListRegex | Indicates 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.allowHeaders | Used 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.ExposeHeaders | Indicates which headers are safe to expose to the API of a CORS API specification. Documentation on MDN. | No |
cors.allowMethods | HTTP defines a set of request methods to indicate the action to be performed for a given resource. Documentation on MDN. | No |
cors.MaxAge | The 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 MDN | No |
headers.request.set | Add custom request headers to an API. | No |
headers.request.delete | Remove a header from an API request. | No |
headers.response.set | Add custom request headers to an API. | No |
headers.response.delete | Remove a header from an API response. | No |
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.
- API Root Object
- API Version 1
- API Version 2
- API Version 3 with Operation Filtering
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
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v1
namespace: apps
spec:
apiName: flight-api
release: v1.0.0
title: "Catch-all"
service:
name: flight-app
port:
number: 3000
openApiSpec:
path: /openapi.yaml
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v2
namespace: apps
spec:
# You're defining a version for the flight-api API
apiName: flight-api
# SemVer of the release
release: v2.0.0
title: "A free text that will be displayed in the portal for that version"
routes:
# The API will be available on one route using URI path for versioning.
# Example: curl https://api.example.com/flight/v2.0.0
- pathPrefix: "/v2.0.0"
# The path prefix of the route will be removed and not forwarded with the request.
stripPathPrefix: true
#The following section mimics the unversioned API configuration
service:
name: flight-svc-v2 # The service that implements version 2 of the API
port:
number: 8080 # The port of the service
openApiSpec:
path: /api/openapi.json # The path to the openAPI specification file
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v3
namespace: apps
spec:
# You're defining a version for the flight-api API
apiName: flight-api
# SemVer of the release
release: v3.0.0
title: "A free text that will be displayed in the portal for that version"
routes:
# The API will be available on one route using URI path for versioning.
# Example: curl https://api.example.com/flight/v2.0.0
- pathPrefix: "/v3.0.0"
# The path prefix of the route will be removed and not forwarded with the request.
stripPathPrefix: true
#The following section mimics the unversioned API configuration
service:
name: flight-svc-v3 # The service that implements version 2 of the API
port:
number: 8080 # The port of the service
openApiSpec:
path: /api/openapi.json # The path to the openAPI specification file
# The operationSet allows members of the operationFilter flight-operations API operations on the path /flights
operationSets:
- name: flight-operations # Name of the operationSet
matchers:
- path: /flights # The operationSet will be applied to all operations under /v3.0.0/flights
Query Parameter Examples
This type of versioning adds one (or multiple) query parameter to the request that indicates the version.
- API Root Object
- API Version 1
- API Version 2
- API Version 3, English Only
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
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v1
namespace: apps
spec:
apiName: flight-api # The root API
release: v1.0.0
title: "A free text that will be displayed in the portal for that version"
service:
name: flight-svc-v1
port:
number: 8080
openApiSpec:
path: /api/openapi.json
# Example of versioning an API using `queryParams` for query parameter versioning.
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v2
namespace: apps
spec:
apiName: flight-api # The root API
release: v2.0.0
title: "A free text that will be displayed in the portal for that version"
routes:
# The API will be available at one route, using a version `query parameter`.
# Example: curl https://api.example.com/flight?version=2.0.0
- queryParams:
version: "2.0.0"
service:
name: flight-svc-v2
port:
number: 8080
openApiSpec:
path: /api/openapi.json
# Example of versioning an API using two `queryParams`, version and language.
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v3-en
namespace: apps
spec:
apiName: flight-api
release: v3.0.0
title: "A free text that will be displayed in the portal for that version"
routes:
# The API will be available at one route, both `queryParams` constraints must match.
# Example: curl https://api.example.com/flight?version=3.0.0&lang=en
- queryParams:
version: "3.0.0"
lang: "en"
service:
name: flight-svc-v3
port:
number: 8080
openApiSpec:
path: /api/openapi.json
Header Examples
In this method, you use a custom header in the request. This leaves the URI of your resources unchanged.
- API Root Object
- API Version 1
- API Version 2
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
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v1
namespace: apps
spec:
apiName: flight-api # The root API
release: v1.0.0
title: "A free text that will be displayed in the portal for that version"
service:
name: flight-svc-v1
port:
number: 8080
openApiSpec:
path: /api/openapi.json
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v2
namespace: apps
spec:
apiName: flight-api
release: v2.0.0
title: "A free text that will be displayed in the portal for that version"
routes:
# The API will be available on one route using a custom header for versioning.
# Example: curl -H "Version: v2.0.0" https://api.example.com/flights
- headers:
Version: "v2.0.0"
service:
name: flights-svc-v2
port:
number: 8080
openApiSpec:
path: /api/openapi.json
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.
- API Root Object
- API Version 1
- API Version 2
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
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v1
namespace: apps
spec:
apiName: flight-api # The root API
release: v1.0.0
title: "A free text that will be displayed in the portal for that version"
service:
name: flight-svc-v1
port:
number: 8080
openApiSpec:
path: /api/openapi.json
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v2
namespace: apps
spec:
apiName: flight-api
release: v2.0.0
title: "A free text that will be displayed in the portal for that version"
routes:
# The API will be available on one route using a `media type` header for versioning.
# Example: curl -H "Content: application/vnd.example.v2.0.0+json" https://api.example.com/flights
- headers:
Content: "application/vnd.example.v2.0.0+json"
service:
name: flights-svc-v2
port:
number: 8080
openApiSpec:
path: /api/openapi.json
port:
number: 8080
CORS
You can define CORS for each APIVersion.
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
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.
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.
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
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
- Path Prefix
- Path Regex
- Only HTTP Method(s)
# 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
# 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
# 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:
- pathRegex: /flights/.*/number
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:
methods:
- GET # Two assigned HTTP methods GET and POST.
- POST
- Only one of the following path settings is allowed:
path
,pathPrefix
, orpathRegex
. - You can use the
methods
option without using a path setting (path
,pathPrefix
, orpathRegex
). - 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.
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
- Two Matchers
operationSets:
- name: read-pets
matchers:
- pathPrefix: /pets
methods:
- GET
operationSets:
- name: read-or-path
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}
.
# 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
.
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}
.
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.
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"