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?

  • You already answered to the question 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?
    – Laiv
    Commented Oct 1, 2016 at 18:14

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.