This question is about optimal REST API design and a problem I'm facing to choose between nested resources and root level collections.
To demonstrate the concept, suppose I have collections City
, Business
, and Employees
. A typical API may be constructed as follows. Imagine that ABC, X7N and WWW are keys, e.g. guids:
GET Api/City/ABC/Businesses (returns all Businesses in City ABC)
GET Api/City/ABC/Businesses/X7N (returns business X7N)
GET Api/City/ABC/Businesses/X7N/Employees (returns all employees at business X7N)
PUT Api/City/ABC/Businesses/X7N/Employees/WWW (updates employee WWW)
This appears clean because it follows the original domain structure - business are in a city, and employees are at a business. Individual items are accessible via key under the collection (e.g. ../Businesses
returns all businesses, while ../Businesses/X7N
returns the individual business).
Here is what the API consumer needs to be able to do:
- Get businesses in a city
(GET Api/City/ABC/Businesses)
- Get all employees at a business
(GET Api/City/ABC/Businesses/X7N/Employees)
- Update individual employee information
(PUT Api/City/ABC/Businesses/X7N/Employees/WWW)
That second and third call, while appearing to be in the right place, use a lot of parameters that are actually unnecessary.
- To get employees at a business, the only parameter needed is the key of the business (
X7N
). - To update an individual employee, the only parameter needed it the key of the employee (
WWW
)
Nothing in the backend code requires non-key information to look up the business or update the employee. So, instead, the following endpoints appear better:
GET Api/City/ABC/Businesses (returns all Businesses in City ABC)
GET Api/Businesses/X7N (returns business X7N)
GET Api/Businesses/X7N/Employees (returns all employees at business X7N)
PUT Api/Employees/WWW (updates employee WWW)
As you can see, I've created a new root for businesses and employees, even though from a domain perspective they are a sub/sub-sub-collection.
Neither solution appears very clean to me.
- The first example asks for unnecessary information, but is structured in a way that appears "natural" to the consumer (individual items from a collection are retrieved via lower leafs)
- The second example only asks for necessary information, but isn't structured in a "natural" way - subcollections are accessible via roots
- The individual employee root would not work when adding a new employee, as we need to know which business to add the employee to, which means that call would at least have to reside under the Business root, such as
POST Api/Businesses/X7N7/Employees
, which makes everything even more confusing.
Is there a cleaner, third way that I'm not thinking of?