spring-web-version

Spring Boot library to manage REST API versioning

License

License

GroupId

GroupId

ru.mihkopylov
ArtifactId

ArtifactId

spring-web-version
Last Version

Last Version

1.0.0
Release Date

Release Date

Type

Type

jar
Description

Description

spring-web-version
Spring Boot library to manage REST API versioning
Project URL

Project URL

https://github.com/mih-kopylov/spring-web-version
Source Code Management

Source Code Management

https://github.com/mih-kopylov/spring-web-version/tree/master

Download spring-web-version

How to add to project

<!-- https://jarcasting.com/artifacts/ru.mihkopylov/spring-web-version/ -->
<dependency>
    <groupId>ru.mihkopylov</groupId>
    <artifactId>spring-web-version</artifactId>
    <version>1.0.0</version>
</dependency>
// https://jarcasting.com/artifacts/ru.mihkopylov/spring-web-version/
implementation 'ru.mihkopylov:spring-web-version:1.0.0'
// https://jarcasting.com/artifacts/ru.mihkopylov/spring-web-version/
implementation ("ru.mihkopylov:spring-web-version:1.0.0")
'ru.mihkopylov:spring-web-version:jar:1.0.0'
<dependency org="ru.mihkopylov" name="spring-web-version" rev="1.0.0">
  <artifact name="spring-web-version" type="jar" />
</dependency>
@Grapes(
@Grab(group='ru.mihkopylov', module='spring-web-version', version='1.0.0')
)
libraryDependencies += "ru.mihkopylov" % "spring-web-version" % "1.0.0"
[ru.mihkopylov/spring-web-version "1.0.0"]

Dependencies

compile (4)

Group / Artifact Type Version
org.springframework.boot : spring-boot-autoconfigure jar 2.3.3.RELEASE
org.springframework.boot : spring-boot-autoconfigure-processor Optional jar 2.3.3.RELEASE
org.springframework.boot : spring-boot-configuration-processor Optional jar 2.3.3.RELEASE
org.projectlombok : lombok Optional jar 1.18.12

provided (3)

Group / Artifact Type Version
org.springframework.boot : spring-boot-starter-logging jar 2.3.3.RELEASE
org.springframework : spring-webmvc jar
org.apache.tomcat.embed : tomcat-embed-core jar 9.0.37

runtime (1)

Group / Artifact Type Version
org.springframework.boot : spring-boot-devtools Optional jar 2.3.3.RELEASE

test (1)

Group / Artifact Type Version
org.springframework.boot : spring-boot-starter-test jar 2.3.3.RELEASE

Project Modules

There are no modules declared in this project.

Spring Web Version

Build Status Maven Central License

When building RESTful API it is worth thinking about API versioning from the early beginning, even if it seems to have a single version forever at the moment. That means API should have a version, and usually the version starts from 1.

At the same time it is worth deciding a way, how version should be passed to the request. Ther're 4 common ways of versioning RESTful API. They're nicely described here: https://restfulapi.net/versioning/. Once one of them is chosen, first controllers can be created. And usually they look like this:

@GetMapping("/v1/employees")
public List<Employee> getEmployees(){}

@PostMapping("/v1/employees")
public Employee createEmployee(){}

and response for GET /v1/employees looks like this

[{}, {}]

Time goes and once it happens that there are too many employees returned for each GET request and pagination is required. The response should look like this:

{
  "content": [{}, {}],
  "page": 0,
  "size": 20,
  "total": 9999
}

And since it's desirable not to break compatibility with the previous application version when a new one is released, API version 2 for GET request is required.

It can be done with just a new mapping like this:

@GetMapping("/v1/employees")
public List<Employee> getEmployeesV1(){}

@GetMapping("/v2/employees")
public Page<Employee> getEmployeesV2(){}
                         
@PostMapping("/v1/employees")
public Employee createEmployee(){}

But in this case there is no /v2 endpoint for creation. Each endpoint has own version. That is not convenient for clients that use this API. It is really convenient for clients to have a single version of API and have no headache with not compatible endpoints versions.

It can also be done with duplicating all unchanged mappings with a new version like this:

@GetMapping("/v1/employees")
public List<Employee> getEmployeesV1(){}

@GetMapping("/v2/employees")
public Page<Employee> getEmployeesV2(){}
                         
@PostMapping("/v1/employees")
public Employee createEmployeeV1(){}

@PostMapping("/v2/employees")
public Employee createEmployeeV2(){
  return createEmployeeV1();
}

But this approach produces a lot of boilerplate code that is really hard to maintain when API is quite big.

And here is where spring-web-version comes to the rescue.

It allowes to add a single new mapping with a new version while all old mappings will handle requests to a new version like this:

@GetMapping("/employees")
@VersionedResource(from=1)
public List<Employee> getEmployeesV1(){}

@GetMapping("/employees")
@VersionedResource(from=2)
public Page<Employee> getEmployeesV2(){}
                         
@PostMapping("/employees")
@VersionedResource(from=1)
public Employee createEmployee(){}

This configuration will map

  • GET /v1/employees to getEmployeesV1 handler
  • GET /v2/employees to getEmployeesV2 handler
  • POST /v1/employees to createEmployee handler
  • POST /v2/employees to createEmployee handler

It's easy to apply it to your web-mvc configuration:

  • add maven dependency
<dependency>
    <groupId>ru.mihkopylov</groupId>
    <artifactId>spring-web-version</artifactId>
    <version>${spring-web-version.version}</version>
</dependency>
  • add spring.mvc.versioning.type to your application.properties file with one of:

    • PATH
    • QUERY
    • HEADER
    • ACCEPT
  • add @VersionedResource annotation for controller class or handler method.

And here you go.

Usage

Version in path prefix

spring.mvc.versioning.type=PATH

Default path prefix is v so URL that starts with v plus version number is matched.

Path prefix can be configured via property: spring.mvc.versioning.pathVersionPrefix=v., so the URL will look like /v.1/employees.

Version in custom query parameter

spring.mvc.versioning.type=QUERY

In this configuration version is passed in query parameter. Default parameter is version, so URL looks like /employees?version=1.

Query parameter can be configured via property spring.mvc.versioning.query=v, so URL will look like /employees?v=1

Version in custom header

spring.mvc.versioning.type=HEADER

In this configuration version is passed in a custom header. Default header name is version.

Header name can be configured via property spring.mvc.versioning.header=VER.

Version in Accept header value

spring.mvc.versioning.type=ACCEPT

In this configuration version is passed inside custom Accept header value, for example "Accept: application/vnd.v1+json".

Header value can also be configured via property spring.mvc.versioning.accept=application/vnd\\+json;version=(\\d+), so accept value will look like application/vnd+json;version=1. The property should contain a regexp pattern with a single group which contains a numeric version.

Minimal API version

Default minimal API version is 1. That means if a request comes with version 0 then an ru.mihkopylov.spring.version.exception.UnsupportedVersionException will be thrown.

Minimal API version can be configured via spring.mvc.versioning.apiMinVersion=10 property

Contribute

Please do contribute! Issues and pull requests are welcome.

Thank you for your help!

Versions

Version
1.0.0
0.3.0
0.2.0
0.1.1
0.1.0