I'm designing a pragmatic REST API and I'm a little stuck on how best to add existing entities to a collection. My domain model includes a Project that has a collection of Sites. This is a strict many-to-many relationship and I have no need to create an entity that explicitly models the relationship (i.e. ProjectSite).

My API will allow consumers to add an existing Site to a Project. Where I'm getting hung up is that the only data I really need are ProjectId and SiteId. My initial idea was:

1. POST myapi/projects/{projectId}/sites/{siteId}

But I also thought about

2. POST myapi/projects/{projectId}/sites

with a Site entity sent as JSON content.

Option 1 is simple and works but doesn't feel quite right, and I have other relationships that cannot follow this pattern so it adds inconsistency to my API.

Option 2 feels better but leads to two concerns:

  • Should I create a Site or throw an exception if a new Site is posted (SiteId = 0)?
  • Because I only need ProjectId and SiteId to create the relationship, the Site could be posted with wrong or missing data for other properties.

A 3rd option is to provide a simple endpoint solely for creating and deleting the relationship. This endpoint would expect a JSON payload containing just ProjectId and SiteId.

What do you think?

  • 2
    See also: stackoverflow.com/questions/2001773/… – Rory Hunter Mar 12 '14 at 15:07
  • @RoryHunter There's some interesting discussion in that link but nothing that eliminates my uncertainty. I especially like that the accepted answer states "You understood right." and the 2nd place (albeit by a large margin) answer "Simply put, you are doing this completely backward." – Jamie Ide Mar 12 '14 at 15:18
  • Your first option is fine though I would use PUT instead of POST as the client is in control of the identity being added to the collection. Your first concern with option 2 is entirely up to you, if you don't want new sites, don't throw an exception but return one of the 4xx codes. Your second concern is neither here nor there. You shouldn't be posting an entire Site anyway unless you allow additions. Adding an existing site should have the id only as you are modifying the site but only the "ProjectSite" collection (even if you don't create a separate resource for it). – Marjan Venema Mar 12 '14 at 17:39

POST is the "append" verb, and also the "processing" verb. PUT is the "create/update" verb (for known identifiers), and almost looks like the right choice here, because the full target URI is known. projectId and siteId already exist, so you don't need to "POST to a collection" to produce a new ID.

The problem with PUT is that it requires the body be the representation of the resource you're PUTting. But the intent here is to append to the "project/sites" collection resource, rather than updating the Site resource.

What if someone PUTs a full JSON representation of an existing Site? Should you update the collection and update the object? You could support that, but it sounds like that's not the intent. As you said,

the only data I really need are ProjectId and SiteId

Rather, I'd try POSTing the siteId to the collection, and rely on the "append" and "process" nature of POST:

POST myapi/projects/{projectId}/sites

{'id': '...' }

Since you're modifying the sites collection resource and not the Site resource, that's the URI you want. POST can know to "append/process" and add the element with that id to the project's sites collection.

That still leaves the door open to creating brand new sites for the project by fleshing out the JSON and omitting the id. "No id" == "create from scratch". But if the collection URI gets an id and nothing else, it's pretty clear what needs to happen.

Interesting question. :)

  • I'm of the faith that believes POST is for create and PUT is for update but your conclusion is where I ended up yesterday. The nice thing is that thanks to attribute routing in Web API, I have the code in a ProjectSites controller so the code is well organized. – Jamie Ide Mar 13 '14 at 15:46
  • I think the defining reason you need to use POST instead of PUT or PATCH here is that you don't have the entire Site entity to put into the sites resource. You only have the id, which requires processing for it to add it to the collection. – crush Feb 22 '19 at 16:48

We use the Patch method for stuff like this. What you want to do is modify an existing Project to add a Site to it.

So something like this would work

PATCH myapi/projects/{id} 

with the Site(s) entity as JSON/JSONArray in the request body.

That way you can use the same URL to modify different parts of the Project if you needed to -- your code in the implementation must be intelligent enough to handle this partial modification of the resource.

  • Interesting approach. I have a "rich" (i.e. highly dependent) older domain model and Project especially has many collections hanging off it. Detecting the entity type being in the request would be a challenge and doesn't fit in with my pragmatic goal. – Jamie Ide Mar 12 '14 at 17:27
  • Why a challenge? If you have those restrictions, you can always use a JSON that makes explicit what it's sending... like {"sites": [], "other-stuff": {}}, you can then branch your code to handle all those "subjsons" very easily. It really depends on your specific problem, but I would still recommend using PATCH as it's designed specifically for these kind of things. – juan Mar 12 '14 at 18:37
  • The downsides I see are 1) API doesn't explicitly communicate which collections allow changes; 2) can't leverage Web API parameter binding; 3) big switch or if statement. – Jamie Ide Mar 12 '14 at 18:58
  • I've never seen the patch method used anywhere else – NimChimpsky Mar 13 '14 at 7:42
  • 1
    Wouldn't PATCH also expect the full entity to be passed as its value here, rather than an id that points to some entity? – crush Feb 22 '19 at 16:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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