That's a very interesting question because tags are kind of a special and even a bit polemic subject.
For example: here at DroneMapp we just implemented a simple tag system where the “tags” field is an Postgres “ArrayField” of strings. That simple. Using a separate table for tags and yet another to relate each tag to each object does work, but sometimes it is too much for most cases.
So, the way we made it, we can simply PATCH the object with the tags list each time, being this list simply a list of strings.
But if you implemented your solution with a separate table and a relation table, remember this: do not create endpoints for relationships. Because the relationships, by themselves, are not what matters on most cases. I would create a “soft field” on the object endpoint that has a list of Tags IDs on the object. So you could PATCH the Tags IDs list and the underlying system should be the responsible for adjusting the relationships.
And, if you already has a kind of a third party algorithm adjusting the relationships, you also could turn your Tags IDs list field into a Tags names list field. That would make the API usage very intuitive, since most of the time your UI alread has a Tags list pre-loaded serving simply for suggesting tags names, and in general tags names are not enforced anyway (only slugfied, maybe).
You could publish a URL like
/v1/object/:object_id/tags/ , too, so adding a tag would be done with a
POST . It seems very nice, but I don't think it's that superior to another solutions since most of the systems out there (and I don't know if that's yours case) have a unified Tags table. So your
POST wouldn't be really creating objects, but relationships to a “hidden” table.
You see? This topic can be really tricky. The only advice I feel really confident to give is this: don't create endpoints to deal directly with relationships. Everyone of this kind I saw proved, sooner or later, to be a bad idea.