Make your REST API resourceful with HATEOAS

HATEOAS stands for Hypermedia As The Engine Of Application State. The ultimate goal of REST architecture is to decouple the client with the server. That means the rest client interacts with the network application without prior knowledge of how to interact with that particular application. HATEOAS decouples the client and server to a large extent which allows both sides to evolve independently.

I'm working on API development project where we had a huge discussion on pros and cons of adding hypermedia in our API. I'm always in favor of adding hypermedia because it helps clients to use an API like a webpage by providing the guidance on what type of content they can retrieve, or what actions they can perform, as well as the appropriate links to do so. Basically, hypermedia includes links, images, video, audio, and texts.

There can always be a debate on for and against of Hypermedia-based API. Mike Stowe has done a great job to put together most of the concerns in his article "API Best Practices: Hypermedia (Part 1 and Part 2)". I'm sure that anyone interested in hypermedia-based API will enjoy these articles.

Example

There are a lot of examples you can find in web illustrating the HATEOAS. In my beginning days of API development, I referred article, from Spring's official site, "Understanding HATEOAS" for an understanding of hypermedia and "Building a Hypermedia-Driven RESTful Web Service" for basic example workout.

I want to add my example extending the above mentioned. The dependency we need to add is (in build.gradle for Gradle building tool):

compile("org.springframework.boot:spring-boot-starter-hateoas")

For this example, I've chosen a movie as a model class. So,

import lombok.*;

import java.io.Serializable;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Movie implements Serializable {
    private static final long serialVersionUID = 2987521091221161580L;
    private long id;
    private String name;
    private String directorName;
    private String rating;
}

The mock repository for the movie is:

@Component
public class MovieRepository {
    private static List<Movie> movies;

    static {
        movies = new ArrayList(){
            {
                add(new Movie(1200L, "Freedom Fighter", "Nelson Mandela","4/5"));
                add(new Movie(1201L, "In Search of Lost Time", "Marcel Proust","3.5/5"));
                add(new Movie(1202L, "Ulysses", "James Joyace","3/5"));
                add(new Movie(1203L, "Hamlet", "William Shakespear","4.5/5"));
            }
        };
    }

    public Optional<Movie> findOneById(Long movieId){
        return movies.stream()
                        .filter(m -> m.getId() == movieId)
                        .findAny();
    }
}

The simple controller class for API endpoint is as follow:

import lombok.extern.slf4j.Slf4j;
import org.springframework.*;

import java.util.Optional;

@RestController
@Slf4j
public class MovieController {
    @Autowiredprivate MovieRepository movieRepository;

    @RequestMapping("/movies/{id}")
    public ResponseEntity<Movie> getBook(@PathVariable("id") Long id) {
        log.trace("getBook({})", id);

        return movieRepository.findOneById(id)
                .map(movie -> {
                    Resource<Movie> movieResource = new Resource<>(movie);
                    movieResource.add(linkTo(methodOn(MovieController.class).getBook(id)).withSelfRel());
                    return new ResponseEntity(movieResource, HttpStatus.OK);
                })
                .orElse(ResponseEntity.notFound().build());
    }
}

Here, we are just wrapping the response object as a resource so that we can add the additional information (link in this case) specifying how to access the resource.

So, if you use any rest client ( I did postman, the simplest and perfect one 🙂 ) to consume the API, with URI like:

http://localhost:8085/movies/1201

then you'll get a response like this:

{
  "id": 1201,
  "name": "In Search of Lost Time",
  "directorName": "Marcel Proust",
  "rating": "3.5/5",
  "_links": {
    "self": {
      "href": "http://localhost:8085/movies/1201"
    }
  }
}

which not only has the required data but also the information about how to access the resource.

You can find all the source code presented above in example on Github.

HATEOAS makes more sense when there are multiple dependencies between resources. Dzone has a good example which you may want to look into.

I believe, sooner or later, hypermedia will be famous in the API world because of its capability to decouple client and server meaningfully. Cheers to hypermedia!!!!

Defend Your Application with Netflix Hystrix
Spring Dependency Injection