What's the right way to represent a search for countries with id or name attributes?
tl;dr: Variant one is a recipe for confusion. If having to choose, use the query string to search.
As far as the REST manifesto is concerned the URI logic is merely an implementation detail - the only concern is that the access mechanism at that location mustn't break over time. Content may be emptied or move - but URI must at all times end up at a service endpoint that gives clients a pre-response they understand and can act on (i.e. 3** redirect or hypermedia re-discovery by the client).
However, many successful and well-designed APIs try to expose an inherently consistent logic, aiming to become intuitively explorable. Therefore your first example api/countries/1/id
is actually BAD regarding developer's experience and market success, as it be confusing to use:
- From looking at the URI, you don't have any clue that you're running a (potentially CPU/Memory-expensive) search and NOT retrieving a single ressource
- Many public APIs follow a logical mapping between resource hierarchy and the exposed URIs - so a common expectation would be that
api/countries/1/id
retrieves {1}
(being a pointless query) or that api/countries/1/name
gives {Italy}
.
Therefore, Query string is an excellent option - especially to offer a Search functionality. One can see the implication from the words ;)
api/countries?name=Italy
api/countries?q=name(Ital*) // same result
api/countries?q=isoCode=(IT) // same result
--> api/countries/1/states // list all states of Italy
api/states?q=country/name(Italy) // - same -
api/states?q=country/id(1) // - same -
The response body of a request can be anticipated from looking at the URI only, which is nice. Better yet: it helps to tell apart resource and view. The "view" is defined in the query string, think of as lens, or filter, through which you look at extracts of what otherwise is one and the same resource (your countries collection).
This pattern is just as good in those regards and also commonly found:
api/countries/search/Italy
api/countries/search/name=Italy
api/countries/search/isoCode=IT,name=*aly
The reason to pick this may revolve around actual implementation details when building your API - as it seperates the direct access to a resource from a search operation - both the required CPU/Memory to fulfill the request, and the response count and format may be fundamentally different.