Suppose I have Project and Task entities for a to-do list. If Tasks are required to be in a Project, the Task routes would probably look like this:
GET /projects/{projectId}/tasks returns all tasks for the specified project
POST /projects/{projectId}/tasks creates a new task in the specified project
But it's not quite as clear (to me) what to do if a Task can be free-standing (not part of a Project). I see a couple of options:
Have two separate routes: one for creating a project-bound task, and one for a free-standing task
POST /projects/{projectId}/tasks creates a new task in the specified project
POST /tasks creates a new free-standing task
This seems...ok. But I don't really like the idea of having to conditionally select an API route on a task-by-task basis.
Have single route and specify an optional projectId in the payload or query params
POST /tasks creates a new task
GET /tasks?projectId={projectId} gets all tasks within the specified project
GET /tasks/{taskId} gets the specified task, regardless of whether it's in a project
This seems better. I like the uniformity of having a single endpoint that handles all of this. With that approach, though, there are two ways to get a task by Id:
GET /tasks/{taskId}
GET /tasks?id=taskId
I get the sense that the first option is a bit more standard, but the second aligns better with other types of filtering one might do (by project, by name, etc.). Is it better to support both, or force one or the other?
Are any of these approaches more generally accepted? Are there other options that I haven't considered?
1 Answer 1
Everybody has difficulties creating the URL structure of the APIs they are building. This is because REST doesn't say how the URLs should look like. In REST you don't even care how the URLs look like because they should be discovered from earlier representation (a.k.a. HATEOAS).
REST or no REST, HATEOAS or no HATEOAS, I do think that focusing on the URL structure of your API is important because it helps you make your API cleaner, easier to understand and to use. But because different people understand REST in different ways, different people also have different opinions on how the URLs should look like.
My personal opinion :) is that this version is the best from the 3:
POST /tasks creates a new task
GET /tasks?projectId={projectId} gets all tasks within the specified project
GET /tasks/{taskId}
The GET /tasks?id=taskId
I would include it in the same bucket as GET /tasks?projectId={projectId}
and wouldn't disallow it or force people to use the URL. A query parameter indicates some sort of search or filtering. Something that can return zero, one or more results. Just because id
is special doesn't mean that it should be excluded and replaced with /tasks/{taskId}
. You can keep both and in the representation of the result you can also have a link with rel=self
to point to /tasks/{taskId}
.
Next, I think a resource should have one URI and /tasks/{taskId}
is that, so again keep it. Besides, you would normally expect that calling /tasks?id=taskId
with a wrong ID would return an empty result set, while /tasks/{taskId}
will return a 404 error.
And finally, I think this version is more flexible than the others and will impact less your API when you need to add stuff to it.
Just my 2 cents from my understanding of REST.
Are any of these approaches more generally accepted?
but it dislike you. And for the second, Do you really need to support both approaches? It does mean that you are not confident on one of them. Like if you were being politically-correct. Why?