Practice for finding a nested resource
Lets say I have a REST resource like:
/companies/{companyId}/departments/{departmentId}/employees/{employeeId}
and entity classes where CompanyEntity has List<DepartementEntity>
and DepartmentEntity has List<EmployeeEntity>
. All IDs are unique.
So now somebody is calling
GET /companies/{companyId}/departments/{departmentId}/employees/{employeeId}
What is a good way to find the employee with {employeeId} in Spring Data JPA / Hibernate?
Way:
employeeRepository.findOne(employeeId);
Pro: Just 1 query
Contra: companyId and departmentId are unused. They can even be random. And this is rather GET /employee/{id} but I want to keep the nested resources with company and department.
Also I would like to access company object to check if the one who is asking is in the same company as the employee.
Way:
company = companyRepository.findOne(companyId);
for(DepartmentEntity department : company.getDepartments()) {
if(department.getId() == departmentId) {
for(EmployeeEntity employee : department.getEmployees()) {
if(employee.getId() == employeeId)
return employee;
}
}
}
Pro: companyId and departementId are considered
Contra: Many queries if you use lazy loading
Thanks.
Just reading through the questions and comments, a few points:
Taking JB Nizet's suggestion more broadly, I think the idea is to use the most straightforward lookup available, and then validate. The validations protect your data from unauthorized access, and also ensure that relational query semantics hold. ie the user would not expect an employee record to be returned if it were not in the specified department; nor if the department were not valid inside the specified company.
So it makes sense (in your implementation, not necessarily in the API) to provide an easy way to get the department for an employee, and the company for the department, so you can perform these validations. Alternatively, each container can have a hashed collection of contained IDs, so you can easily determine if a given employee ID is within the department.employees collection, and same with department in company.departments.
It's not unusual, and it's often helpful, to have nested collections like this. Relational Endpoints is one name for this API design pattern. But I agree with Oliver Gierke that you should provide users a direct path to a known entity. Different ways to factor this into your design:
/departments
and /employees
collections. In results returned from the nested resources, you can include a canonical link, as the pattern suggests. So GET /companies/123/departments/456/employees/789
could return a representation of employee, along with a canonical
link (in the header or body, depending on your wire format) to /employees/789
, and the client can use this for subsequent access to the employee record. GET /companies/{companyId}/departments
returns a collection of hyperlinks to departments within the company; where each hyperlink is to the canonical URL, eg /departments/456
. No need for further levels of nesting, because employees, and everything else you need to know about the department, are available through the canonical resource. 上一篇: RESTful API在同一个URI上执行多个操作时
下一篇: 寻找嵌套资源的练习