skip to content

How does Spring Data REST expose derived query (search) methods, and how do you control their paths and parameters?

level: seniorimportance: should knowfreq 35%

answer

  1. finders live under /{repo}/search
  2. @Param names the query parameters
  3. @RestResource path/rel renames; exported=false hides
  4. Page + Pageable -> paging/sorting on search
  5. CRUD is NOT under /search

basics

~20 s

Derived finder methods like findByLastName are exposed under /{repo}/search. Each method becomes /{repo}/search/{name} with its parameters as query params. Use @Param to name the query parameters, @RestResource(path/rel) to rename the endpoint, and exported=false to hide a finder.

solid answer

~40 s

When a repository declares derived query methods (`findByLastName`, `findByAgeGreaterThan`, etc.), Spring Data REST collects them under a dedicated **search resource**: `GET /people/search` lists them in HAL, and each is invoked at `GET /people/search/{path}?param=value`. The method's parameters must be annotated with `@Param("name")` so SDR can bind them to query parameters — without `@Param` the parameter name may be unavailable and binding fails. By default the endpoint path is the method name; `@RestResource(path = "...", rel = "...")` renames it, and `@RestResource(exported = false)` hides a specific finder from `/search`. Methods returning collections support paging/sorting when they return `Page`/accept `Pageable`. Only *query* methods land under /search — CRUD save/delete are served by the collection/item resources, not here.

code

java · 15 lines
java
public interface PersonRepository extends JpaRepository<Person, Long> {

    // GET /people/search/findByLastName?lastName=Smith
    List<Person> findByLastName(@Param("lastName") String lastName);

    // Friendly path + rel: GET /people/search/by-name?name=Sm
    @RestResource(path = "by-name", rel = "byName")
    Page<Person> findByLastNameStartingWith(@Param("name") String name,
                                            Pageable pageable);

    // Present in Java, hidden from /people/search
    @RestResource(exported = false)
    List<Person> findBySsn(@Param("ssn") String ssn);
}
// GET /people/search  -> HAL listing of byName + findByLastName (not findBySsn)

go deeper

for a junior

Know that custom finder methods show up under a /search URL automatically.

for a middle

Explain the /{repo}/search/{method} structure and the need for @Param on parameters.

for a senior

Cover path/rel renaming, exported=false, paging via Page/Pageable, and that CRUD isn't under /search.

for a principal

Curate search endpoints as part of the API contract — friendly rels/paths, hide sensitive finders, and prefer projections over leaking full entities.

**The search resource.** Beyond CRUD, a Spring Data repository often has *derived query methods* — methods whose implementation Spring Data generates from their name, e.g. `List<Person> findByLastName(String last)`. Spring Data REST exposes all such exported query methods under a single sub-resource per repository: `GET /people/search`. Hitting that URL returns a HAL document whose `_links` list every available finder. Each finder is then callable at `GET /people/search/{methodPath}` with its arguments supplied as **query parameters**. **Binding parameters with @Param.** SDR maps HTTP query parameters to method arguments by name. Because Java historically erases parameter names, you should annotate each argument with `@Param("lastName")`; the client then calls `/people/search/findByLastName?lastName=Smith`. Omitting `@Param` risks a failure to resolve the parameter name (unless `-parameters` compilation is in effect, but `@Param` is the reliable, documented approach). Missing required parameters yield a client error. **Customizing with @RestResource.** On the query method: - `path` renames the URL segment under `/search` (e.g. `findByLastNameStartingWith` → `path = "nameStarts"` → `/people/search/nameStarts`). - `rel` renames the link relation in the `/search` HAL listing. - `exported = false` removes that finder from `/search` entirely while keeping it usable in Java. **Paging and sorting.** If a finder returns `Page<Person>` and accepts a `Pageable`, SDR supports `?page=`, `?size=`, and `?sort=field,dir` on that search endpoint and returns HAL page metadata. Returning `List` gives an unpaged result. **What is and isn't exposed here.** Only *query/derived (and @Query) methods* appear under `/search`. The standard CRUD operations from `CrudRepository`/`JpaRepository` (findAll, findById, save, deleteById) are surfaced by the collection and item resources, **not** the search resource — so you won't see `save` under `/search`. Methods that aren't query methods (e.g. arbitrary default methods) aren't exposed as searches. **Projections on searches.** You can apply a `@Projection` via `?projection=name` to shape the returned representation of search results, just as with collection/item resources. **Gotchas.** (1) Forgetting `@Param` is the most common cause of 'parameter cannot be resolved' errors on search endpoints. (2) The default path is the *full method name* — unfriendly URLs like `/search/findByLastNameIgnoreCaseContaining`; rename with `path`. (3) Renaming `rel` breaks hypermedia clients that discover the finder by its relation. (4) `exported=false` on a query method hides it from `/search` but does not affect CRUD. (5) A finder that returns a single entity vs a collection changes whether the result is embedded or a bare item — design accordingly.

  • Why annotate search-method parameters with @Param?
    SDR binds HTTP query parameters to method arguments by name; @Param provides that name reliably regardless of compiler settings, so /search/findByLastName?lastName=X resolves correctly. Without it the parameter name may be unavailable and binding fails.
  • Will save() or findAll() appear under /people/search?
    No. Only derived/query (@Query) methods are listed under /search. CRUD operations are handled by the collection (/people) and item (/people/{id}) resources.

saying these in an interview costs you the question

  • Expecting CRUD methods like save under /search
  • Omitting @Param and expecting query-parameter binding to work
  • Assuming search arguments are path variables rather than query params

context