API Design – Best Practices for API Endpoints for an Entity

api-designentityentity-frameworkrest

I don't know if the title is reflecting the question correctly, but I can explain more than write a good title.

If I have a database that has (employees, departments, orders, etc…), and have many ways to call the endpoints of this system, and each way requests data less or more of the same entity (e.g employee entity), let's say I'm building the employees endpoints:

  • An endpoint to show all employees for managing purposes.
  • One to be used in dropdowns on UI to choose one employee (for some reason), and only should show IsActive = true employees.
  • Need one because in one page for a specific role he should be able to see only his employees not all of them (and at the other hand another role allowed to see all the employees).

This kind of requirements is normal and repeated in a lot of systems, but I'm lost and need to know when to put the filters and is it ok to use one endpoint and provide many filters for it (like one for getting only { id, name } but with filter to be able to get only IsActive = true or all employees), or should I separate the endpoints ? and depending on what exactly ?

I know that I can use GraphQL or OData and it'll ease the things for me and for consumers of my API, is it the only solution ?

Best Answer

I don't think there is universal agreement on this, but I use the following rules-of-thumb:

  • If it makes sense to request the complete list of a particular resource (all employees), then there should be a top-level endpoint for that.
  • If you need to filter on a attribute of a resource, then use a query parameter for that or create a separate endpoint. The choice mostly depends on how common it is to need the filter and if there is a obvious name for the endpoint.
  • If you need to filter on a relation between two resources, then use a new endpoint below resource you filter on. (For example, the endpoint for the employees that all report to a particular manager could be /managers/<id>/employees/)
  • To specify that you only want a sub-set of the attributes in the resource representation, use the query string.

This would, for example give the queries

GET /employees
GET /employees?active=true
GET /role/<id>/employees?fields=id,name

Sometimes, it might also make sense to invert the filtering logic, so that GET /employees returns only the active employees and GET /employees?include_inactive gives the list of active and inactive employees.

In the end, try to design your interface such that the common case is easiest to get right for the clients.