API Version
Manage the life cycle of the API, handling multiple versions.
Introduction
API versioning is crucial for managing changes, updates, and improvements to APIs over time. Traefik Hub provides robust support for effectively managing multiple API versions while maintaining backward compatibility and supporting existing clients.
The APIVersion Object
The APIVersion object represents a specific version of an API within Traefik Hub. It allows API owners to effectively manage the lifecycle of their APIs.
When creating a new APIVersion object, you must reference it in its parent API's versions
field.
You can expose an APIVersion using the same mechanism as exposing an API, either through an Ingress or IngressRoute annotation: hub.traefik.io/api-version
.
An API and its APIVersions must be part of the same namespace.
Example
Let's start with an existing API exposed through an Ingress.
- API
- Ingress
apiVersion: hub.traefik.io/v1alpha1
kind: API
metadata:
name: flight-api
namespace: apps
spec:
openApiSpec:
path: /openapi.json
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: flight-ingress
namespace: apps
annotations:
hub.traefik.io/api: flight-api
spec:
rules:
- http:
paths:
- pathType: Prefix
path: /flight
backend:
service:
name: flight-service
port:
number: 8080
Now, see an example of how you can add two versions to this API, each attached to different Ingresses with the routing mechanism of your choice. Then, the API object only needs to reference its supported versions. Note that the previous API object now references its versions.
- API
- API Version 1
- Ingress 1
- API Version 2
- Ingress 2
apiVersion: hub.traefik.io/v1alpha1
kind: API
metadata:
name: flight-api
namespace: apps
spec:
# Remove the openApiSpec section
versions: # Reference the versions (assumed to be in the same namespace)
- name: flight-api-v1
- name: flight-api-v2
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v1
namespace: apps
spec:
title: "Flight API v1.0.0"
release: v1.0.0
openApiSpec:
path: /openapi.json
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: flight-ingress
namespace: apps
annotations:
hub.traefik.io/api-version: flight-api-v1
spec:
rules:
- http:
paths:
- pathType: Prefix
path: /flight
backend:
service:
name: flight-service
port:
number: 8080
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v2
namespace: apps
spec:
title: "Flight API v2.0.0"
release: v2.0.0
openApiSpec:
path: /openapi.json
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: flight-ingress
namespace: apps
annotations:
hub.traefik.io/api-version: flight-api-v2
spec:
rules:
- http:
paths:
- pathType: Prefix
path: /flight/v2
backend:
service:
name: flight-v2-service
port:
number: 8080
And that's it!
Title & Description
The APIVersion object supports title
and description
fields. These fields are shown in the API portal and they override the corresponding values in the API version’s OpenAPI specification.
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: hello-api-v1
namespace: apps
spec:
title: "Hello API v1.0.0"
description: "A simple API version for greeting users."
release: v1.0.0
openApiSpec:
path: /openapi.yaml
Operation Filtering
Operation filtering allows you to restrict user groups to specific OpenAPI operations.
Here's how it works:
- Define OperationSets: OperationSets are named collections of OpenAPI operations. For example, you might create an operationSet called "admin" that includes operations to delete customers and retrieve customer statistics, and another called "support" that includes operations to retrieve a list of all customers.
- Configure APIAccess: Once you've defined your
operationSets
, you can reference them in your APIAccess configurations. For instance, you might grant access to the "admin" operationSet to members of the "admin" group and access to the "support" operationSet to members of the "support" group.
OperationSets
An operationSet
is composed of:
- A name, which will be referenced in the APIAccess.
- A matcher to select the API operation(s) to be included in the
operationSet
.
Here are examples of how to define operationSets using different matchers:
- Path
- Path Prefix
- Path Regex
- Only HTTP Method(s)
# Example of versioning an API using `path` for URI path.
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: my-flights-api-v2
namespace: apps
spec:
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
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:
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
openApiSpec:
path: /api/v2/openapi.json
operationSets:
- name: flight-operations
matchers:
- pathPrefix: /flights
# Example of versioning an API using `pathRegex` for URI path.
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: my-flights-api-v2
namespace: apps
spec:
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
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:
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
openApiSpec:
path: /api/v2/openapi.json
operationSets:
- name: flight-operations
matchers:
methods:
- GET
- POST
- Only one of the following path settings is allowed:
path
,pathPrefix
, orpathRegex
. - You can use the
methods
option without specifying a path setting (path
,pathPrefix
, orpathRegex
). - You can 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 user request.
Please note the subtle difference between configuring one matcher with multiple options and configuring multiple matchers. The examples below illustrate 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:
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
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:
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
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 the other 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:
release: v2.0.0
title: "An awesome title for this release, like a cheese name"
openApiSpec:
path: /api/v2/openapi.json
operationSets:
- name: read-flights
matchers:
- pathPrefix: /flights
- methods:
- GET
validateRequestMethodAndPath
Like the API resource, an APIVersion
can enforce only the paths and methods that are explicitly defined in its OpenAPI specification by using validateRequestMethodAndPath
.
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v2
namespace: apps
spec:
release: v2.0.0
openApiSpec:
path: /openapi.json
validateRequestMethodAndPath: true
If validateRequestMethodAndPath
is true, requests for any undefined path or method result in 404 Not Found
status code. If it is not defined or is false
, then the global static setting applies.
The global setting is false by default.
Global Static Configuration
Similar to an API, the APIVersion
respects the global configuration only if validateRequestMethodAndPath
isn’t explicitly set. However, Any per-APIVersion setting overrides the global default.
- Static Configuration
- CLI
hub:
apiManagement:
openApi:
validateRequestMethodAndPath: true
--hub.apiManagement.openApi.validateRequestMethodAndPath=true
CORS
If an API version must be accessed from a different domain than the API Portal or client application, you can enable CORS through the cors
field of an APIVersion
resource.
When the cors
field is absent, no CORS headers are applied.
- APIVersion with CORS
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: my-api-version
namespace: default
spec:
release: v1.0.0
openApiSpec:
path: /openapi.yaml
cors:
allowHeadersList:
- "*"
allowOriginsList:
- "*"
allowMethodsList:
- "*"
exposeHeadersList:
- "*"
addVaryHeader: true
allowCredentials: true
allowOriginListRegex:
- ".*"
maxAge: 86400
Advanced API Versioning Examples
Traefik Hub provides the most flexible API versioning on the market. By leveraging Traefik’s intelligent routing, you can define custom rules that route requests to different APIVersion
objects.
This enables versioning based on host, URI path, HTTP method, query parameters, headers, and more—even in combination with logical operators ( AND (&&) and OR (||)).
Below are some examples to illustrate how you might route requests to different versions of your API based on various criteria.
Each example references a separate APIVersion
resource through annotations like hub.traefik.io/api-version
.
Path-Based Versioning
The most common approach is to embed the version in the path (e.g., /v1
or /v2
).
- Ingress with Path-Based Versioning
- APIVersion
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: flight-ingress-v2
namespace: apps
annotations:
hub.traefik.io/api-version: flight-api-v2
spec:
rules:
- http:
paths:
- path: /flight/v2
pathType: Prefix
backend:
service:
name: flight-service
port:
number: 8080
apiVersion: hub.traefik.io/v1alpha1
kind: APIVersion
metadata:
name: flight-api-v2
namespace: apps
spec:
release: v2.0.0
openApiSpec:
path: /openapi.json
Outcome: Requests to http://<HOST>/flight/v2/...
will map to the flight-api-v2
resource.
Host-Based Versioning
You can also route by host, so that v1.api.example.com
goes to one version and v2.api.example.com
goes to another.
- IngressRoute with Host-Based Versioning
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: flight-ir-v2
namespace: apps
annotations:
hub.traefik.io/api-version: flight-api-v2
spec:
entryPoints:
- web
routes:
- match: Host(`v2.api.example.com`)
kind: Rule
services:
- name: flight-service
port: 8080
Outcome: Any request matching Host: v2.api.example.com
will be routed to flight-api-v2
. Meanwhile, you could have another IngressRoute
for v1.api.example.com
that references flight-api-v1
.
Header-Based Versioning
Sometimes, clients specify the API version via a custom HTTP header. For example, X-API-Version: v2
.
- IngressRoute with Header-Based Versioning
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: flight-ir-header
namespace: apps
annotations:
hub.traefik.io/api-version: flight-api-v2
spec:
routes:
- match: Headers(`X-API-Version`, `v2`)
kind: Rule
services:
- name: flight-service
port: 8080
Outcome: Here, any request carrying X-API-Version: v2
in its header is routed to flight-api-v2
. Another route could handle X-API-Version: v1
.
Content Negotiation / Media Type Versioning
You can also version your API by examining the Accept header for specific media types. In many cases, clients include a version within the media type itself. For example, a request might specify:
Accept: application/vnd.example+json;version=1.0
or
Accept: application/vnd.example.v1+json
Below is an example of an IngressRoute that routes requests to flight-api-v3
if the Accept header matches one of these media types.
- IngressRoute with Content Negotiation Versioning
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: flight-ir-content-neg
namespace: apps
annotations:
hub.traefik.io/api-version: flight-api-v3
spec:
routes:
- match: Headers(`Accept`, `application/vnd.example+json;version=1.0`) || Headers(`Accept`, `application/vnd.example.v1+json`)
kind: Rule
services:
- name: flight-service
port: 8080
Outcome: Here, the route applies if the Accept header matches either application/vnd.example+json;version=1.0
or application/vnd.example.v1+json
. The version is part of the media type, so version information
is specified in the request header. You can adjust these rules to include additional media types or other version identifiers.
Query Parameter Versioning
You can also route based on query parameters, for example ?version=v2
.
- IngressRoute with Query Parameter Versioning
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: flight-ir-query
namespace: apps
annotations:
hub.traefik.io/api-version: flight-api-v2
spec:
routes:
- match: Query(`version`, `v2`)
kind: Rule
services:
- name: flight-service
port: 8080
Outcome: Here, any request to, for instance, /flight?version=v2
goes to flight-api-v2
.
Combining Rules & Logical Operators
One of the most powerful aspects of Traefik Hub’s routing is that you can combine matchers with &&
(logical AND) or ||
(logical OR). For example, you might require both a host and a header to match:
- IngressRoute with Query Parameter Versioning
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: flight-ir-combined
namespace: apps
annotations:
hub.traefik.io/api-version: flight-api-v2
spec:
routes:
- match: Host(`api.example.com`) && Headers(`X-API-Version`, `v2`)
kind: Rule
services:
- name: flight-service
port: 8080
Outcome: Here, the route applies only if the Host is api.example.com
and the request includes X-API-Version: v2
. You can adjust or expand these conditions to match content type.
TLS information, or any other rule Traefik Hub supports.
Complex Multi-Condition Versioning
You can layer multiple rules together to target a single API version. For instance, the example below routes requests to flight-api-v3
only if all of these conditions are true:
- The host is
secure.api.example.com
- The
X-Client-Feature
header value includes the word “beta” (regex match) - The request uses either
POST
orPUT
, or the query parameter?feature=beta
is present
- IngressRoute with a Complex Multi-Condition Routing
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: flight-ir-complex
namespace: apps
annotations:
hub.traefik.io/api-version: flight-api-v3
spec:
entryPoints:
- websecure
routes:
- match: Host(`secure.api.example.com`) && HeadersRegex(`X-Client-Feature`,`.*beta.*`) && (Method(`POST`,`PUT`) || Query(`feature`, `beta`))
kind: Rule
services:
- name: flight-service
port: 8080
Although the release field in APIVersion
accepts semver-like values (v1.0.0, v2.0.0, etc.), the versioning logic is entirely up to your routing rules. The semver field is for internal tracking and clarity.
How requests are routed to a given APIVersion
object depends on the Ingress or IngressRoute annotations and the routing criteria you specify.
With Traefik Hub's approach to API versioning, you have the flexibility to implement about any versioning strategy. Whether you need path-based, host-based, or even a custom scheme with specific headers, Traefik Hub can handle it all.