Whether it’s social media platforms, e-commerce websites, or mobile applications, they all rely on efficient communication between different systems to deliver dynamic and interactive experiences. One of the key technologies that powers this communication is the REST API.

REST, which stands for Representational State Transfer, is an architectural style that provides a set of principles for designing networked applications. It has gained immense popularity due to its simplicity, scalability, and ease of integration. Java, being a robust and versatile programming language, offers excellent support for building RESTful APIs.

In this article, we’ll explore the fundamental concepts of REST, dive into the specifics of building RESTful APIs in Java, and provide some examples to solidify our understanding.

So, let’s embark on this journey to unravel the world of REST API in Java and discover its significance in modern web development.

Understanding REST API

When it comes to building web applications, REST plays a crucial role in facilitating communication between different systems over the internet. RESTis a guiding set of principles for designing networked applications. It emphasizes simplicity, scalability, and the use of standard protocols.
At its core, REST treats everything as a resource. A resource can be any piece of information or functionality that can be accessed and manipulated.

These resources are identified by unique URLs (which do not change over time), often referred to as endpoints, and are represented using various media types, such as JSON (JavaScript Object Notation) or XML (eXtensible Markup Language). Notice how we’re talking about “representation” here, which is already part of the name of this architectural style.

RESTful architectures follow a client-server model, where the client initiates requests to the server, and the server responds with the requested data or performs the requested actions. This decoupling of the client and server enables a more flexible and scalable system.

  • Stateless Communication: One of the fundamental principles of REST is statelessness. In a RESTful API, each request from the client to the server must contain all the information necessary to understand and process that request. The server does not store any client-specific state, ensuring that each request can be processed independently. This simplifies server implementation and improves scalability.
  • Client-Server Separation: REST emphasizes the separation of concerns between the client and the server. The client is responsible for using the data returned by the server, however it needs to, but is the server the only one responsible for any processing that needs to be done with the resource data. This separation allows for independent evolution and scalability of the client and server components.
  • Uniform Interface: RESTful APIs should have a uniform interface, meaning that they follow a consistent set of conventions for interacting with resources. This includes using standard HTTP methods (GET, POST, PUT, DELETE) to perform CRUD (Create, Read, Update, Delete) operations on resources. Additionally, the APIs should use standard HTTP status codes and headers to convey the outcome and metadata of requests and responses.
  • Resource-Based Interaction: Resources are the key building blocks of RESTful APIs. Each resource should have a unique identifier (URL) and should be accessed and manipulated through standard and consistent representations. The API should provide a clear and consistent structure for organizing and navigating resources.
  • Hypermedia as the Engine of Application State (HATEOAS) : HATEOAS is an important concept in REST. It means that the API should provide links to related resources within its responses. These links provide discoverability and enable the client to navigate the API’s capabilities dynamically. The client can explore the API by following these hypermedia links, rather than having prior knowledge of all possible endpoints.
  • <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.1.1</version>                 <relativePath/>                 <!-- lookup parent from repository -->         </parent>         <groupId>com.example</groupId>         <artifactId>fdoglio</artifactId>         <version>0.0.1-SNAPSHOT</version>         <name>fdoglio</name>         <description>Demo rest api project</description>         <properties>                 <java.version>17</java.version>         </properties>         <dependencies>                 <dependency>                         <groupId>org.springframework.boot</groupId>                         <artifactId>spring-boot-starter</artifactId>                 </dependency>                 <dependency>                         <groupId>org.springframework</groupId>                         <artifactId>spring-web</artifactId>                         <version>6.0.10</version>                 </dependency>                 <dependency>                         <groupId>org.springframework.boot</groupId>                         <artifactId>spring-boot-starter-test</artifactId>                         <scope>test</scope>                 </dependency>                 <dependency>                         <groupId>org.springframework.boot</groupId>                         <artifactId>spring-boot-starter-web</artifactId>                 </dependency>         </dependencies>         <build>                 <plugins>                         <plugin>                                 <groupId>org.springframework.boot</groupId>                                 <artifactId>spring-boot-maven-plugin</artifactId>                         </plugin>                 </plugins>         </build> </project>

    For our API, we’ll need to create three classes:

    import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Repository; @Repository public class UserRepository {         private final List<User> users;         UserRepository() {                 this.users = new ArrayList<>();                 users.add(new User(1, "John Doe", "john@example.com"));             users.add(new User(2, "Jane Smith", "jane@example.com")); users.add(new User(3, "Bob Johnson", "bob@example.com")); public List<User> findAll() { return users; public boolean add(User user) {         users.add(user);         return true; public boolean deleteUser(Long id) {          for (int i = 0; i < users.size(); i++) { User user = users.get(i); if (user.getID() == id) { users.remove(i); return true;          return false; public User updateUser(Long id, User updatedUser) {          for (int i = 0; i < users.size(); i++) { User user = users.get(i); if (user.getID() == id) { users.set(i, updatedUser); return updatedUser;                  return null;

    In the end, this class is pretty standard, the only relevant highlight is the `@Repository` annotation which tells SpringBoot that this is the class in charge of data storage. That said, this annotation would be more useful if we were fully integrating JPA and a real database.

    Let’s now create our API.

    Building our REST controller

    As I already mentioned, our controller class will be the entry point for our application. This means our server will be started and our methods will be automatically mapped to the endpoints we defined above.

    Let’s see what that looks like in code:

    package com.example.fdoglio;
    import java.awt.PageAttributes.MediaType;
    import java.util.List;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.; import org.springframework.web.bind.annotation.;
    @RestController
    @RequestMapping(produces = "application/json")
    public class UserController {
            private final UserRepository userRepository;
            UserController(UserRepository userRepo) {
                    this.userRepository = userRepo;
            @GetMapping(value = "/users")
            public List getUsers() {
                    List users = userRepository.findAll();
                    return users;
            @PostMapping(value = "/users")
            public ResponseEntity newUser(@RequestBody User user) {
                    this.userRepository.add(user);
                    return new ResponseEntity(user, HttpStatus.CREATED);
            @PutMapping(value = "/users/{id}")
            public ResponseEntity updateUser(@PathVariable Long id, @RequestBody User user) {
                    User retUser = this.userRepository.updateUser(id, user);
                    if(retUser != null) {
                            return new ResponseEntity(user, HttpStatus.ACCEPTED);                        
                    return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
            @DeleteMapping(value = "/users/{id}")
            public ResponseEntity deleteUser(@PathVariable Long id) {
                    boolean result = this.userRepository.deleteUser(id);
                    if(result) {
                            return ResponseEntity.noContent().build();
                    } else {
                            return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
    

    Our controller class, without the annotations, is quite simple, every method uses the userRepository to perform their respective action, and they return a ResponseEntity as a result. As part of the return values, you can also appreciate the use of the HttpStatus enum, which shows how we’re using the standard HTTP Status codes mentioned as part of the REST explanation at the start of this guide.

    Finally, once we add the annotations, things start to take shape:

  • The @RequestMapping annotation here is used to specify the content type of the responses from all methods. We could, if we wanted, also specify this information on each method, but since we’re going to be replying with JSONs on every method, it makes more sense to define it at class level.
  • The @GetMapping, @PostMapping, @PutMapping and @DeleteMapping annotations clearly map the method they annotate to the HTTP Method they reference. As part of the annotation, you also specify the URI, so you have control over what the endpoints look like. Keep in mind that as part of REST, your URIs should reference resources, so in this example, they all take the shape of “/users” or “/users/123”, which either references the list of users, or a single one. Other URLs, such as “/users/edit”, or “/users/delete” would not be correct, because they would reference actions instead of resources.
  • Another interesting detail about route mapping with SpringBoot, is that URL parameters, such as the ID of the user are easily mapped to method parameters. So if we send a DELETE request to /users/123 we’ll be receiving the value 123 on the id parameter of the deleteUser method
  •