Skip to content
geeksforgeeks
  • Courses
    • DSA to Development
    • Get IBM Certification
    • Newly Launched!
      • Master Django Framework
      • Become AWS Certified
    • For Working Professionals
      • Interview 101: DSA & System Design
      • Data Science Training Program
      • JAVA Backend Development (Live)
      • DevOps Engineering (LIVE)
      • Data Structures & Algorithms in Python
    • For Students
      • Placement Preparation Course
      • Data Science (Live)
      • Data Structure & Algorithm-Self Paced (C++/JAVA)
      • Master Competitive Programming (Live)
      • Full Stack Development with React & Node JS (Live)
    • Full Stack Development
    • Data Science Program
    • All Courses
  • Tutorials
    • Data Structures & Algorithms
    • ML & Data Science
    • Interview Corner
    • Programming Languages
    • Web Development
    • CS Subjects
    • DevOps And Linux
    • School Learning
  • Practice
    • Build your AI Agent
    • GfG 160
    • Problem of the Day
    • Practice Coding Problems
    • GfG SDE Sheet
  • Contests
    • Accenture Hackathon (Ending Soon!)
    • GfG Weekly [Rated Contest]
    • Job-A-Thon Hiring Challenge
    • All Contests and Events
  • System Design Tutorial
  • What is System Design
  • System Design Life Cycle
  • High Level Design HLD
  • Low Level Design LLD
  • Design Patterns
  • UML Diagrams
  • System Design Interview Guide
  • Scalability
  • Databases
Open In App
Next Article:
Session Management in Microservices
Next article icon

REST API Versioning in Java Microservices

Last Updated : 20 May, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

REST API is widely used when it comes to communicating between two microservices. And thus maintaining it is again a major task. We also have to make sure that while developing new functionalities we don't interrupt the working functionalities because it may create a major problem in other services. Thus we have to do versioning of the API which adds up the new features keeping others as it is.

Versioning of API

Versioning means that we create a new endpoint with new functionality or enhance the old functionality. Let's say We have a method named "getBooks" which will return all the books present in the database at once. Now as new books start adding into the database and retrieving all at once seems to take more time we build a new method known as "getPaginatedBooks". This method will return the only books that were asked by a user by specifying page no. and size.

But when implementing new functionality we should not revoke the old endpoints because it might be used at some place where it plays a major role. Thus keeping them both in an application is only the solution, and thus we will give the version number so that the user or other developer gets to know that for a new application, they have to use a new Versioned API.

Need of Versioning

Most important need is to keep running the old method for old customers while introduce new functionality for new customers. Here is the list of important points for which the versioning of API is must.

  • Maintaining Compatibility: Versioning allows us to keep existing customers to continue using the API without disruption, even as new features are added.
  • Isolating Changes: By introducing versioning, changes made to one version of the API won't affect other versions, ensuring stability.
  • Granular Control: With versioning, we as developer have granular control over which features are available to different clients.
  • Clear Communication: With Versioning we can give clear description of APIs to our customer that whether the APIs is obsolete or it is in experiment.

Methods of Versioning APIs

There are different ways in which we can create versions in APIs. Widely used methods are explained below

1. URL Versioning

In this approach we will add version number in the URL.

  • If the normal URL looks like http://localhost:8080/get-books,
  • Then it will become http://localhost:8080/v1/get-books and http://localhost:8080/v2/get-books.

Note: Problem with this approach is that it will clutter the URL and thus this is not good for long term maintainability.

2. Query Parameter Versioning

Here we will append the query parameter named as Version or any of your choice which is not used any where else. And as a value we will give its version number.

Example:

Before: http://localhost:8080/get-books

After: http://localhost:8080/get-books?version=1.0 and http://localhost:8080/get-books?version=2.0

This will keep the main URL but it will add the bottleneck of parameter and thus sometimes it may be less intuitive for clients.

3. Header Versioning

This method is used when we don't want change the URL any how by now adding version number in URL or by adding query parameter. So thus we will add one Header in Request known as "X-API-Version". So the request will look likes

GET /get-books HTTP/1.1
Host: localhost:8080
Accept: application/json
X-API-Version: 1

Here the URL is not tempered but the only problem is that client may require additional effort to manage the version header.

Let's understand this with some real time examples and for that here we are taking a example of Library Management System where we have a microservice which manages book. And now we have to create a another version of some APIs.

Implementation of REST API Versioning in Microservices

We have Spring Boot application in which we have written the APIs. The code for creating a whole Application is shown below.

Step 1: Create new Spring Boot Application from any IDE or Text Editor.

Step 2: Create a Table in Database with below query:

CREATE TABLE IF NOT EXISTS public.books
(
bookid integer NOT NULL PRIMARY KEY,
title character varying(255),
author character varying(255) ,
category character varying(255),
quantity integer, availablequantity integer,
)


pom.xml file:

XML
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>     <parent>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-parent</artifactId>         <version>3.2.5</version>         <relativePath /> <!-- lookup parent from repository -->     </parent>     <groupId>com.lms</groupId>     <artifactId>LibraryManagementSystem</artifactId>     <version>0.0.1-SNAPSHOT</version>     <name>LibraryManagementSystem</name>     <description>Demo project for Spring Boot</description>     <properties>         <java.version>17</java.version>     </properties>     <dependencies>         <dependency>             <groupId>org.springdoc</groupId>             <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>             <version>2.5.0</version>         </dependency>          <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency>          <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-data-jpa</artifactId>         </dependency>          <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-test</artifactId>             <scope>test</scope>         </dependency>          <!--Package of the Database you are using-->          <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-devtools</artifactId>         </dependency>         <dependency>             <groupId>org.jetbrains</groupId>             <artifactId>annotations</artifactId>             <version>RELEASE</version>             <scope>compile</scope>         </dependency>       </dependencies>       <build>         <plugins>             <plugin>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-maven-plugin</artifactId>             </plugin>         </plugins>     </build>  </project> 


In this application, we have also integrated Swagger, if you don't want that then feel free to remove those package and install all other packages.

Connection string in application.properties:

spring.application.name=LibraryManagementSystem
spring.datasource.url= YOUR CONNECTION STRING
spring.datasource.username= USERNAME
spring.datasource.password= PASSWORD


Main class:

Java
import io.swagger.v3.oas.models.info.Info; import org.springdoc.core.customizers.OpenApiCustomizer; import org.springdoc.core.customizers.OperationCustomizer; import org.springdoc.core.models.GroupedOpenApi; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean;   @SpringBootApplication() public class LibraryManagementSystemApplication {      public static void main(String[] args) {         SpringApplication.run(LibraryManagementSystemApplication.class, args);     }      //It will create a version in Swagger if you are not going with     //swagger then remove all this beans     @Bean     public GroupedOpenApi version1() {         return GroupedOpenApi.builder()                 .group("LMS-api-1.0")                 .pathsToMatch("/v1/**")                 .addOpenApiCustomizer(setDocumentation("1.0"))                 .build();     }      @Bean     public GroupedOpenApi version2() {         return GroupedOpenApi.builder()                 .group("LMS-api-2.0")                 .pathsToMatch("/v2/**")                 .addOpenApiCustomizer(setDocumentation("2.0"))                 .build();     }       @Bean     public GroupedOpenApi headerVersionedApi() {         return GroupedOpenApi.builder()                 .group("Header-Versioned-API")                 .pathsToMatch("/**")                 .addOperationCustomizer(getHeadersVersionedApi())                 .addOpenApiCustomizer(setDocumentation(""))                 .build();     }       private OpenApiCustomizer setDocumentation(String version)     {         return  (openApi -> {             openApi.info(new Info().title("Library Management System").version(version).description("This is the another version of LMS"));         });     }      //This will return only those methods which have required header "X-API-VERSION"     private OperationCustomizer getHeadersVersionedApi() {         return (operation, handlerMethod) -> {             var parameters = operation.getParameters();             if(parameters!=null && parameters.stream().anyMatch(parameter -> {                 return (parameter.getName().equals("X-API-VERSION")||(parameter.getName().equals("version")));             }))             {                 return operation;             }              return null;         };     } } 


Controller:

Java
import com.lms.LibraryManagementSystem.Models.Books; import com.lms.LibraryManagementSystem.Services.BookServices; import com.lms.LibraryManagementSystem.ViewModels.BookViewModel; import org.springdoc.core.annotations.ParameterObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.data.domain.Pageable;  import java.util.List;   @RestController public class BookController {      @Autowired     BookServices bookServices;      //It will return all the books. Also, this is v1 API     @GetMapping(value="/v1/get-all-books")     public ResponseEntity<List<Books>> getAllBook() {         List<Books> list = bookServices.GetAllBooks();         return ResponseEntity.ok(list);     }      //API for Adding book in Database     @PostMapping("/v1/addBook")     public ResponseEntity<String> addBook(@RequestBody BookViewModel model)     {         bookServices.addBook(model);         return  ResponseEntity.ok("Book Added Successfully");     }      //API for Deleting Book     @DeleteMapping("/v1/delete-book/{id}")     public ResponseEntity<String> deleteBook(@PathVariable("id") Integer id) {          bookServices.deleteBook(id);         return  ResponseEntity.ok("Book Deleted Successfully");     }      //API for Editing book     @PutMapping("/v1/editBook")     public ResponseEntity<Object> editBook(@RequestBody Books model)     {         var result = bookServices.editBook(model);         return result.<ResponseEntity<Object>>map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.badRequest().body("Book not Found"));      }      //This will return only book whose id is passed as parameter     @GetMapping("/v2/get-book/{id}")     public ResponseEntity<Object> getBook(@PathVariable("id") Integer id) {         var book = bookServices.GetBook(id);         return book.<ResponseEntity<Object>>map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.badRequest().body("Book Not Found"));      }      //Return all the Books with page number and page size     @GetMapping("/v2/get-all-books")     public ResponseEntity<List<Books>> getBooks(@ParameterObject Pageable pageSize)     {         Page<Books> list = bookServices.GetBooks(pageSize);         return ResponseEntity.ok(list.toList());     }      //Header Versioned API     //This v1 API which returns all books     @GetMapping(value = "/get-all-books", headers = "X-API-VERSION=1.0")     public ResponseEntity<List<Books>> getBooksByHeaders()     {         List<Books> list = bookServices.GetAllBooks();         return ResponseEntity.ok(list);     }      //This v2 API which returns books based on Page no. and Page size     @GetMapping(value = "/get-all-books", headers = "X-API-VERSION=2.0")     public ResponseEntity<List<Books>> getBooksByHeaders(@ParameterObject Pageable pageSize)     {         Page<Books> list = bookServices.GetBooks(pageSize);         return ResponseEntity.ok(list.toList());     }           //These are all Parameter versioned API      //This v1 API which returns all books     @GetMapping(value = "/get-all-booksPara", params = "version=1.0")     public ResponseEntity<List<Books>> getBooksByParam()     {         List<Books> list = bookServices.GetAllBooks();         return ResponseEntity.ok(list);     }      //This v2 API which returns only book which is on given Page     @GetMapping(value = "/get-all-booksPara", params = "version=2.0")     public ResponseEntity<List<Books>> getBooksByParam(@ParameterObject Pageable pageSize)     {         Page<Books> list = bookServices.GetBooks(pageSize);         return ResponseEntity.ok(list.toList());     } } 


Service File:

Java
import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import java.util.List; import java.util.Optional;  import com.lms.LibraryManagementSystem.Repository.Implementations.BookRepository; import com.lms.LibraryManagementSystem.ViewModels.BookViewModel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;  import com.lms.LibraryManagementSystem.Models.Books;  @Service public class BookServices {     @Autowired     BookRepository bookRepository;          //Returns all the books present in the database     public List<Books> GetAllBooks()     {         return bookRepository.findAll();     }          public Optional<Books> GetBook(Integer id)     {         //Find the book and return the Optional Book object         return bookRepository.findById(id);     }      public void addBook(@org.jetbrains.annotations.NotNull BookViewModel model)     {         //Create an object of Books from BookViewModel         Books book = new Books();         book.setAuthor(model.authorName);         book.setCategory(model.category);         book.setQuantity(model.quantity);         book.setAvailableQuantity(model.availableQuantity);         book.setTitle(model.bookName);          bookRepository.addBook(book);         return;     }      public Optional<Books>  editBook(@org.jetbrains.annotations.NotNull Books model)     {         Optional<Books> result = bookRepository.findById(model.getBookId());                  //Checks whether books is Present and if it is present than update them or return the result.          if(result.isPresent())         {              Books book = result.get();              book.setAuthor(model.getAuthor());             book.setCategory(model.getCategory());             book.setQuantity(model.getQuantity());             book.setAvailableQuantity(model.getAvailableQuantity());             book.setTitle(model.getTitle());              bookRepository.editBook(book);             return result;         }         return result;     }      public void deleteBook(int id)     {         Optional<Books> book = bookRepository.findById(id);          //Delete the book if Present else do nothing         book.ifPresent(books -> bookRepository.delete(books));          return;     }      public Page<Books> GetBooks(Pageable pageSize) {         //Find the books based on Pageable object         return bookRepository.findAll(pageSize);     } } 


Repository file:

Java
import com.lms.LibraryManagementSystem.Models.Books; import com.lms.LibraryManagementSystem.Repository.Contracts.IBookRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.stereotype.Repository;  import org.springframework.data.domain.Pageable; import java.util.List; import java.util.Optional;  @Repository public class BookRepository {     @Autowired     IBookRepository bookRepository;       public List<Books> findAll() {         return bookRepository.findAll();     }      public Optional<Books> findById(Integer id) {         return  bookRepository.findById(id);     }      public Page<Books> findAll(Pageable pageSize) {         return bookRepository.findAll((org.springframework.data.domain.Pageable) pageSize);     }      public void addBook(Books model)     {         bookRepository.save(model);         return;     }      public void delete(Books book) {         bookRepository.delete(book);     }      public void editBook(Books book) {         bookRepository.save(book);     }   } 


Books Model:

Java
import jakarta.persistence.*;  @Entity public class Books {     @Id     @GeneratedValue(strategy = GenerationType.IDENTITY)     @Column(name = "bookid")     private int bookId;      @Column(name = "title")     private String title;      @Column(name = "author")     private String author;      @Column(name = "category")     private String category;      @Column(name = "quantity")     private int quantity;      @Column(name = "availablequantity")     private int availableQuantity;      // Constructor     public Books() {}      // Getters and setters     public int getBookId() {         return bookId;     }      public void setBookId(int bookId) {         this.bookId = bookId;     }      public String getTitle() {         return title;     }      public void setTitle(String title) {         this.title = title;     }      public String getAuthor() {         return author;     }      public void setAuthor(String author) {         this.author = author;     }      public String getCategory() {         return category;     }      public void setCategory(String category) {         this.category = category;     }      public int getQuantity() {         return quantity;     }      public void setQuantity(int quantity) {         this.quantity = quantity;     }      public int getAvailableQuantity() {         return availableQuantity;     }      public void setAvailableQuantity(int availableQuantity) {         this.availableQuantity = availableQuantity;     } } 


BooksViewModel:

Java
public class BookViewModel {     public  String bookName;      public String authorName;      public String category;      public int quantity;      public  int availableQuantity; } 


By doing this, we can run the application.

Index Page - V1 URL Versioned API


Similar to this, we have more 2 dropdown option of Version 2 APIs and another one is Headers versioned APIs.

Version 2 APIs


So by this we can separate the versions clearly with good documentation. Now let's explore get-all-books api for better understanding.

Version 1 of get-all-books:

Version 1 Get all Books


So there is not parameter as expected and it will return all the books from the database. Which will take the time and resources and thus we can implement another API for paginated books.


Version 2 of get-all-books:

Version 2

So as parameter was Pageable we have three parameter: page, size and sort. Which will return only the books which satisfy this conditions. Now with this our old endpoints are still there in place and by adding this won't disrupt that.

Header Versioned API

Now let's look at Header versioned API.

Header Versioned API


So there is one parameter X-API-VERSION which ensures keeping the versioning of API properly the other parameters are for meeting condition of different version it will take the parameter only if that version is going to receive it.

Parameter based Versioning

Let's see how parameter based versioning works.

Version 1

So we have given the version with the query string and for demonstration. We have also given the size parameter but it won't affect it anyhow.

But if we give the size with Version 10 it will retrieve only 10 entries from the database.

size 10
Version 2

Advance Versioning Technique

Content Negotiation:

In this method, we will version the APIs based on the type produced by endpoint. Which means based on the Accept type in the request the Method will be called.

GET /resource HTTP/1.1
Host: api.example.com
Accept: application/vnd.example.v1+json

If the above request is made then the Method producing will be called.

Conclusion

Versioning is very important when developing large software's because it is very necessary that you update the apis on regular basis for better result and thus this approaches are very helpful at that time.


Next Article
Session Management in Microservices

A

agrawalvishesh9271
Improve
Article Tags :
  • Advance Java
  • Java Microservices

Similar Reads

  • Session Management in Microservices
    Session Management in Microservices explains how to handle user sessions in a microservices architecture. Microservices break down an application into smaller, independent services, making session management more complex. The article covers various methods to manage sessions effectively, ensuring us
    11 min read
  • API Gateway Security Best Practices in Java Microservices
    An API Gateway acts as a front-end for receiving API requests, enforcing throttling and security policies, passing requests to the back-end service, and then passing the response back to the requester. It sits between external clients and microservices, providing a unified entry point for multiple s
    4 min read
  • Java Microservices Interview Questions and Answers
    Microservices, also called Microservices Architecture, is a software development approach that involves building large applications as a collection of small functional modules. This architectural approach is widely adopted due to its ease of maintenance and faster development process. Microservices
    15+ min read
  • Request-driven vs Event-driven Microservices
    Microservices architecture has transformed the way of software development, enabling teams to build scalable, resilient applications. Understanding the various communication patterns is crucial. Two predominant models are request-driven and event-driven microservices. Table of Content What are Micro
    5 min read
  • Java Microservices Tutorial
    In this Java Microservices Tutorial, we will guide you through the essentials of building efficient microservices architecture using Java. Whether you are an experienced Java developer or just starting, this microservices tutorial will provide you with key insights to design, implement, and scale Ja
    8 min read
  • Resilient Microservices Design
    Resilient Microservices Design explores creating tough, adaptable systems from small, independent parts. Imagine a city's infrastructure, each building operates independently, yet collaborates seamlessly. Similarly, microservices are like small city blocks, each serving a specific purpose. This arti
    8 min read
  • Reducing Latency in Microservices
    Reducing Latency in Microservices explains how to make microservices faster and more responsive. Microservices are small, independent services that work together to create a larger application. Sometimes, these services can slow down due to high latency, which is the time it takes for data to travel
    11 min read
  • API Gateway Patterns in Microservices
    In the Microservices Architecture, the API Gateway patterns stand out as a crucial architectural tool. They act as a central hub, managing and optimizing communication between clients and multiple microservices. These patterns simplify complexity, enhance security, and improve performance, making th
    11 min read
  • Orchestration vs. Choreography in Microservices
    In software development, especially when dealing with microservices architecture, the ideas of orchestration and choreography are very important for managing how services interact with each other. These two methods offer different ways to integrate services, each with its benefits and suitable scena
    4 min read
  • API Composition Pattern in Microservices
    In today's world of software development, microservices have become a popular way to build applications. One effective approach within this architecture is the API Composition Pattern. This pattern allows developers to combine multiple microservices into a single, unified API response. By doing so,
    14 min read
geeksforgeeks-footer-logo
Corporate & Communications Address:
A-143, 7th Floor, Sovereign Corporate Tower, Sector- 136, Noida, Uttar Pradesh (201305)
Registered Address:
K 061, Tower K, Gulshan Vivante Apartment, Sector 137, Noida, Gautam Buddh Nagar, Uttar Pradesh, 201305
GFG App on Play Store GFG App on App Store
Advertise with us
  • Company
  • About Us
  • Legal
  • Privacy Policy
  • In Media
  • Contact Us
  • Advertise with us
  • GFG Corporate Solution
  • Placement Training Program
  • Languages
  • Python
  • Java
  • C++
  • PHP
  • GoLang
  • SQL
  • R Language
  • Android Tutorial
  • Tutorials Archive
  • DSA
  • Data Structures
  • Algorithms
  • DSA for Beginners
  • Basic DSA Problems
  • DSA Roadmap
  • Top 100 DSA Interview Problems
  • DSA Roadmap by Sandeep Jain
  • All Cheat Sheets
  • Data Science & ML
  • Data Science With Python
  • Data Science For Beginner
  • Machine Learning
  • ML Maths
  • Data Visualisation
  • Pandas
  • NumPy
  • NLP
  • Deep Learning
  • Web Technologies
  • HTML
  • CSS
  • JavaScript
  • TypeScript
  • ReactJS
  • NextJS
  • Bootstrap
  • Web Design
  • Python Tutorial
  • Python Programming Examples
  • Python Projects
  • Python Tkinter
  • Python Web Scraping
  • OpenCV Tutorial
  • Python Interview Question
  • Django
  • Computer Science
  • Operating Systems
  • Computer Network
  • Database Management System
  • Software Engineering
  • Digital Logic Design
  • Engineering Maths
  • Software Development
  • Software Testing
  • DevOps
  • Git
  • Linux
  • AWS
  • Docker
  • Kubernetes
  • Azure
  • GCP
  • DevOps Roadmap
  • System Design
  • High Level Design
  • Low Level Design
  • UML Diagrams
  • Interview Guide
  • Design Patterns
  • OOAD
  • System Design Bootcamp
  • Interview Questions
  • Inteview Preparation
  • Competitive Programming
  • Top DS or Algo for CP
  • Company-Wise Recruitment Process
  • Company-Wise Preparation
  • Aptitude Preparation
  • Puzzles
  • School Subjects
  • Mathematics
  • Physics
  • Chemistry
  • Biology
  • Social Science
  • English Grammar
  • Commerce
  • World GK
  • GeeksforGeeks Videos
  • DSA
  • Python
  • Java
  • C++
  • Web Development
  • Data Science
  • CS Subjects
@GeeksforGeeks, Sanchhaya Education Private Limited, All rights reserved
We use cookies to ensure you have the best browsing experience on our website. By using our site, you acknowledge that you have read and understood our Cookie Policy & Privacy Policy
Lightbox
Improvement
Suggest Changes
Help us improve. Share your suggestions to enhance the article. Contribute your expertise and make a difference in the GeeksforGeeks portal.
geeksforgeeks-suggest-icon
Create Improvement
Enhance the article with your expertise. Contribute to the GeeksforGeeks community and help create better learning resources for all.
geeksforgeeks-improvement-icon
Suggest Changes
min 4 words, max Words Limit:1000

Thank You!

Your suggestions are valuable to us.

What kind of Experience do you want to share?

Interview Experiences
Admission Experiences
Career Journeys
Work Experiences
Campus Experiences
Competitive Exam Experiences