Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manage the logic behind the API versioning?

I want to identify what might be considered as a best practice for URI versioning of the APIs, regarding the logic of the back-end implementation.

Let's say we have a java application with the following API:

http://.../api/v1/user
Request:    
    {
      "first name": "John",
      "last name": "Doe"
    }

After a while, we need to add 2 more mandatory fields to the user API:

http://.../api/v2/user
Request:    
    {
      "first name": "John",
      "last name": "Doe",
      "age": 20,
      "address": "Some address"
    }

We are using separate DTOs for each version, one having 2 fields, and another having 4 fields.

We have only one entity for the application, but my question is how we should handle the logic, as a best practice? Is ok to handle this in only one service?

If those 2 new fields "age" and "address" would not be mandatory, this would not be considered a breaking change, but since they are, I am thinking that there are a few options:

  • use only one manager/service in the business layer for all user API versions (but the complexity of the code in that only one manager will grow very much in time and will be hard to maintain)
  • use only one manager for all user API versions and also use a class as a translator so I can make compatible older API versions with the new ones
  • a new manager/service in the business layer for each user API version

If I use only one manager for all user API versions and put there some constraints/validations, V2 will work, but V1 will throw an exception because those fields are not there.

I know that versioning is a big topic, but I could not find a specific answer on the web until now. My intuition says that having a single manager for all user API versions will result in a method that has nothing to do with clean code, and also, I am thinking that any change added with a new version must be as loosely coupled as possible, because will be easier to make older methods deprecated and remove them in time.

like image 428
Alex Avatar asked Oct 11 '19 06:10

Alex


2 Answers

You are correct in your belief that versioning with APIs is a contentious issue. You are also making a breaking change and so incrementing the version of your API is the correct decision (w.r.t. semver).

Ideally your backend code will be under version control (eg GitHub). In this case you can safely consider V1 to be a specific commit in your repository. This is the code that has been deployed and is serving traffic for V1. You can then continue making changes to your code as you see fit. At some point you will have added some new breaking changes and decide to mark a specific commit as V2. You can then deploy V2 alongside V1. When you decide to depreciate V1 you can simply stop serving traffic.

You'll need some method of ensuring only V1 traffic goes to the V1 backend and V2 to the V2 backend. Generally this is done by using a Reverse Proxy; popular choices include NGINX and Apache. Any sufficient reverse proxy will allow you to direct requests based on the path such that if the request is prefixed by /api/v1 then redirect that request to Backend1 and if prefixed by /api/v2 to Backend2.

Hopefully this model will help keep your code clean: the master branch in your repository only needs to deal with the most recent API. If you need to make changes to older API versions this can be done with relative ease: branch off the V1 commit, make your changes, and then define the HEAD of that modified branch as the 'new' V1.

A couple of assumptions about your backend have been made for this answer that you should be aware about. Firstly, your backend can be scaled horizontally. For example, this means that if you interact with a database then the multiple versions of your API can all safely access the database concurrently. Secondly, that you have the resources do deploy replica backends.

Hopefully that explanation makes sense; but if not any questions send them my way!

like image 171
jontypreston Avatar answered Oct 05 '22 03:10

jontypreston


If you're able to/can entertain code changes to your existing API, then you can refer to this link. Also, the link's mentioned at the bottom of the post direct you to respective GitHub source code which can be helpful in case if you think to introduce the code changes after your trial-error.

The mentioned approach(using @JsonView) basically prevents one from introducing multiple DTO's of a single entity for the same/multiple clients. Eventually, one can also refrain from introducing new version APIs each & every time you introduce new fields in your existing API.

spring-rest-jackson-jsonview
jackson-jsonview

like image 22
Aniket Avatar answered Oct 05 '22 03:10

Aniket