Customizing Spring Data REST

There are many options to tailor Spring Data REST. These subsections show how.

Customizing Item Resource URIs

By default, the URI for item resources are comprised of the path segment used for the collection resource with the database identifier appended. That lets you use the repository’s findOne(…) method to lookup entity instances. As of Spring Data REST 2.5, this can be customized by using configuration API on RepositoryRestConfiguration (preferred on Java 8) or by registering an implementation of EntityLookup as a Spring bean in your application. Spring Data REST picks those up and tweaks the URI generation according to their implementation.

Assume a User with a username property that uniquely identifies it. Further assume that we have a Optional<User> findByUsername(String username) method on the corresponding repository.

On Java 8, we can register the mapping methods as method references to tweak the URI creation, as follows:

@Component
public class SpringDataRestCustomization implements RepositoryRestConfigurer {

  @Override
  public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
    config.withEntityLookup()
      .forRepository(UserRepository.class)
      .withIdMapping(User::getUsername)
      .withLookup(UserRepository::findByUsername);
  }
}

forRepository(…) takes the repository type as the first argument, a method reference mapping the repositories domain type to some target type as the second argument, and another method reference to map that value back by using the repository mentioned as the first argument.

If you are not running Java 8 or better, you could use the method, but it would require a few quite verbose anonymous inner classes. On older Java versions, you should probably prefer implementing a UserEntityLookup that resembles the following:

@Component
public class UserEntityLookup extends EntityLookupSupport<User> {

    private final UserRepository repository;

    public UserEntityLookup(UserRepository repository) {
        this.repository = repository;
    }

    @Override
    public Serializable getResourceIdentifier(User entity) {
        return entity.getUsername();
    }

    @Override
    public Object lookupEntity(Serializable id) {
        return repository.findByUsername(id.toString());
    }
}

Notice how getResourceIdentifier(…) returns the username to be used by the URI creation. To load entity instances by the value returned from that method, we now implement lookupEntity(…) by using the query method available on the UserRepository.

Customizing repository exposure

By default, all public Spring Data repositories are used to expose HTTP resources as described in Repository resources. Package protected repository interfaces are excluded from this list, as you express its functionality is only visible to the package internally. This can be customized by explicitly setting a RepositoryDetectionStrategy (usually through the enum RepositoryDetectionStrategies) on RepositoryRestConfiguration. The following values can be configured:

  • ALL — exposes all Spring Data repositories regardless of their Java visibility or annotation configuration.

  • DEFAULT — exposes public Spring Data repositories or ones explicitly annotated with @RepositoryRestResource and its exported attribute not set to false.

  • VISIBILITY — exposes only public Spring Data repositories regardless of annotation configuration.

  • ANNOTATED — only exposes Spring Data repositories explicitly annotated with @RepositoryRestResource and its exported attribute not set to false.

If you need custom rules to apply, simply implement RepositoryDetectionStrategy manually.

Customizing supported HTTP methods

Customizing default exposure

By default, Spring Data REST exposes HTTP resources and methods as described in Repository resources based on which CRUD methods the repository exposes. The repositories don’t need to extend CrudRepository but can also selectively declare methods described in aforementioned section and the resource exposure will follow. E.g. if a repository does not expose a delete(…) method, an HTTP DELETE will not be supported for item resources.

If you need to declare a method for internal use but don’t want it to trigger the HTTP method exposure, the repository method can be annotated with @RestResource(exported = false). Which methods to annotate like that to remove support for which HTTP method is described in Repository resources.

Sometimes managing the exposure on the method level is not fine-grained enough. E.g. the save(…) method is used to back POST on collection resources, as well as PUT and PATCH on item resources. To selectively define which HTTP methods are supposed to be exposed, you can use RepositoryRestConfiguration.getExposureConfiguration().

The class exposes a Lambda based API to define both global and type-based rules:

ExposureConfiguration config = repositoryRestConfiguration.getExposureConfiguration();

config.forDomainType(User.class).disablePutForCreation(); (1)
config.withItemExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.PATCH)); (2)
1 Disables the support for HTTP PUT to create item resources directly.
2 Disables the support for HTTP PATCH on all item resources.