angularjsrestspring-data-resthateoashal-json

Bookmark AngularJS frontend (direct access) - Spring Data Rest backend


I've started to implement a web-front end to manipulate a RESTful service served by Spring Data Rest over a traditional relational DB. This is done with AngularJS and the angular-hal library that I found fits very well the HATEOAS philosophy and formalism of Spring Data Rest.

For example, I just need to know the first API endpoint ('/'), and then all of my queries are done through the relations without caring for the urls. The problem I have is to be able to directly access the page displaying one of my entities.

Let's take the example of a contacts repository. If I start from the home page, then navigate through the contact list, then choose the contact I want to see in details, there is no problem.

But I can't access directly the page showing the details of the contact: the resource is injected from the list controller to the edit controller, and the edit controller is not able to know the url to request if not told by the list controller.

The root problem is that with Spring Data Rest, entities have no public field for their id (not in JSON), and the repositories have no API relation to search by id.

I thought about some solutions, but I don't like any of them.

1. Work around in the backend

2. Use self relation

3. Forget about HATEOAS

So, am I missing something ? What is the best practice regarding the access of data in a full RESTFul HATEOAS environment ?


Solution

  • I've found the exposeIdsFor methods that makes possible to add the field annotated with @Id in JSON.

    @Configuration
    public class RepositoryConfiguration  extends SpringBootRepositoryRestMvcConfiguration {
    
        /**
         * add here the main resources that would need direct access from outside
         */
        @Override
        protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
            config.exposeIdsFor(Contact.class, User.class);
        }
    }
    

    Then I made all the entities I want the id to be exposed to inherit from a common abstract class

    @MappedSuperclass
    public abstract class AbstractEntity {
    
        @Id @GeneratedValue
        Long id;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    }
    

    this abstract class is served by a @NoRepositoryBean repository

    @NoRepositoryBean
    public interface AbstractEntityRepository<T extends AbstractEntity> extends JpaRepository<T, Long> {    
        @RestResource(rel = "byId")
        T findById(@Param("id") Long id);
    }
    

    and then I can expose both id and method byId() for the entities I care :

    @Entity
    public class Contact extends AbstractEntity {
    
        @Column 
       String name;
    
        @Column 
        String email;
    }
    
    public interface ContactRepository extends AbstractEntityRepository<Contact> {    
    }
    

    I think this makes a good work around and allow the direct access of the entities from the client at a small price