As far as I can tell each individual resource should have only one canonical path. So in the following example what would good URL patterns be?
Take for example a rest representation of Companies. In this hypothetical example, each company owns 0 or more departments and each department owns 0 or more employees.
A department can't exist without an associated company.
An employee can't exist without an associated department.
Now I'd find the natural representation of the resource patterns to be.
/companies A collection of companies - Accepts POST for a new company. Get for the entire collection.
/companies/{companyId} An individual company. Accepts GET, PUT and DELETE
/companies/{companyId}/departments Accepts POST for a new item. (Creates a department within the company.)
/companies/{companyId}/departments/{departmentId}/
/companies/{companyId}/departments/{departmentId}/employees
/companies/{companyId}/departments/{departmentId}/employees/{empId}
Given the constraints, in each of the sections, I feel that this makes sense if a bit deeply nested.
However, my difficulty comes if I want to list (GET
) all employees across all companies.
The resource pattern for that would most closely map to /employees
(The collection of all employees)
Does that mean that I should have /employees/{empId}
also because if so then there are two URI's to get the same resource?
Or maybe the entire schema should be flattened but that would mean that employees are a nested top-level object.
At a basic level /employees/?company={companyId}&department={deptId}
returns the exact same view of employees as the most deeply nested pattern.
What's the best practice for URL patterns where resources are owned by other resources but should be query-able separately?
I've tried both design strategies - nested and non-nested endpoints. I've found that:
if the nested resource has a primary key and you don't have its parent primary key, the nested structure requires you to get it, even though the system doesn't actually require it. nested endpoints typically require redundant endpoints. In other words, you will more often than not, need the additional /employees endpoint so you can get a list of employees across departments. If you have /employees, what exactly does /companies/departments/employees buy you? nesting endpoints don't evolve as nicely. E.g. you might not need to search for employees now but you might later and if you have a nested structure, you have no choice but to add another endpoint. With a non-nested design, you just add more parameters, which is simpler. sometimes a resource could have multiple types of parents. Resulting in multiple endpoints all returning the same resource. redundant endpoints makes the docs harder to write and also makes the api harder to learn.
In short, the non-nested design seems to allow a more flexible and simpler endpoint schema.
What you have done is correct. In general there can be many URIs to the same resource - there are no rules that say you shouldn't do that.
And generally, you may need to access items directly or as a subset of something else - so your structure makes sense to me.
Just because employees are accessible under department:
company/{companyid}/department/{departmentid}/employees
Doesn't mean they can't be accessible under company too:
company/{companyid}/employees
Which would return employees for that company. It depends on what is needed by your consuming client - that is what you should be designing for.
But I would hope that all URLs handlers use the same backing code to satisfy the requests so that you aren't duplicating code.
/company/3/department/2/employees/1
). If the api provides ways to get each resource, then making each of those requests could be done in either a client side library or as a one-off endpoint that reuses code.
/company/*
should only return the company resource and not change resource type at all. None of this is specified by REST - its generally a poorly specified - just personal preference.
I've moved what I've done from the question to an answer where more people are likely to see it.
What I've done is to have the creation endpoints at the nested endpoint, The canonical endpoint for modifying or querying an item is not at the nested resource.
So in this example (just listing the endpoints that change a resource)
POST /companies/ creates a new company returns a link to the created company.
POST /companies/{companyId}/departments when a department is put creates the new department returns a link to /departments/{departmentId}
PUT /departments/{departmentId} modifies a department
POST /departments/{deparmentId}/employees creates a new employee returns a link to /employees/{employeeId}
So there are root level resources for each of the collections. However the create is in the owning object.
POST
meaning PUT
, and otherwise.
I've read all of the above answers but it seems like they have no common strategy. I found a good article about best practices in Design API from Microsoft Documents. I think you should refer.
In more complex systems, it can be tempting to provide URIs that enable a client to navigate through several levels of relationships, such as /customers/1/orders/99/products. However, this level of complexity can be difficult to maintain and is inflexible if the relationships between resources change in the future. Instead, try to keep URIs relatively simple. Once an application has a reference to a resource, it should be possible to use this reference to find items related to that resource. The preceding query can be replaced with the URI /customers/1/orders to find all the orders for customer 1, and then /orders/99/products to find the products in this order.
.
Tip Avoid requiring resource URIs more complex than collection/item/collection.
I disagree with this kind of path
GET /companies/{companyId}/departments
If you want to get departments, I think it's better to use a /departments resource
GET /departments?companyId=123
I suppose you have a companies
table and a departments
table then classes to map them in the programming language you use. I also assume that departments could be attached to other entities than companies, so a /departments resource is straightforward, it's convenient to have resources mapped to tables and also you don't need as many endpoints since you can reuse
GET /departments?companyId=123
for any kind of search, for instance
GET /departments?name=xxx
GET /departments?companyId=123&name=xxx
etc.
If you want to create a department, the
POST /departments
resource should be used and the request body should contain the company ID (if the department can be linked to only one company).
GET /companies/{companyId}?include=departments
, since this allows both the company and its departments to be fetched in a single HTTP request. Fractal does this really well.
/departments
endpoint to only be accessible by an admin, and have each company access their own departments only through ` /companies/{companyId}/departments`
How your URLs look have nothing to do with REST. Anything goes. It actually is an "implementation detail". So just like how you name your variables. All they have to be is unique and durable.
Don't waste too much time on this, just make a choice and stick to it/be consistent. For example if you go with hierarchies then you do it for all your resources. If you go with query parameters...etc just like naming conventions in your code.
Why so ? As far as I know a "RESTful" API is to be browsable (you know..."Hypermedia as the Engine of Application State"), therefore an API client does not care about what your URLs are like as long as they're valid (there's no SEO, no human that needs to read those "friendly urls", except may be for debugging...)
How nice/understandable a URL is in a REST API is only interesting to you as the API developer, not the API client, as would the name of a variable in your code be.
The most important thing is that your API client know how to interpret your media type. For example it knows that :
your media type has a links property that lists available/related links.
Each link is identified by a relationship (just like browsers know that link[rel="stylesheet"] means its a style sheet or rel=favico is a link to a favicon...)
and it knowns what those relationships mean ("companies" mean a list of companies,"search" means a templated url for doing a search on a list of resource, "departments" means departments of the current resource )
Below is an example HTTP exchange (bodies are in yaml since it's easier to write):
Request
GET / HTTP/1.1
Host: api.acme.io
Accept: text/yaml, text/acme-mediatype+yaml
Response: a list of links to main resource (companies, people, whatever...)
HTTP/1.1 200 OK
Date: Tue, 05 Apr 2016 15:04:00 GMT
Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT
Content-Type: text/acme-mediatype+yaml
# body: this is your API's entrypoint (like a homepage)
links:
# could be some random path https://api.acme.local/modskmklmkdsml
# the only thing the API client cares about is the key (or rel) "companies"
companies: https://api.acme.local/companies
people: https://api.acme.local/people
Request: link to companies (using previous response's body.links.companies)
GET /companies HTTP/1.1
Host: api.acme.local
Accept: text/yaml, text/acme-mediatype+yaml
Response: a partial list of companies (under items), the resource contains related links, like link to get the next couple of companies (body.links.next) an other (templated) link to search (body.links.search)
HTTP/1.1 200 OK
Date: Tue, 05 Apr 2016 15:06:00 GMT
Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT
Content-Type: text/acme-mediatype+yaml
# body: representation of a list of companies
links:
# link to the next page
next: https://api.acme.local/companies?page=2
# templated link for search
search: https://api.acme.local/companies?query={query}
# you could provide available actions related to this resource
actions:
add:
href: https://api.acme.local/companies
method: POST
items:
- name: company1
links:
self: https://api.acme.local/companies/8er13eo
# and here is the link to departments
# again the client only cares about the key department
department: https://api.acme.local/companies/8er13eo/departments
- name: company2
links:
self: https://api.acme.local/companies/9r13d4l
# or could be in some other location !
department: https://api2.acme.local/departments?company=8er13eo
So as you see if you go the links/relations way how you structure the path part of your URLs does't have any value to your API client. And if your are communicating the structure of your URLs to your client as documentation, then your are not doing REST (or at least not Level 3 as per "Richardson's maturity model")
Rails provides a solution to this: shallow nesting.
I think this is a good because when you deal directly with a known resource, there's no need to use nested routes, as has been discussed in other answers here.
as per django rest framework documentation:
Generally we recommend a flat style for API representations where possible, but the nested URL style can also be reasonable when used in moderation.
https://www.django-rest-framework.org/api-guide/relations/#example_2
Success story sharing