REST API Design – Best Pattern for Adding an Existing Item to a Collection

api-designrest

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?

Best Answer

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. :)

Related Topic