Treating id changes with JSON:API

I have the use case that PATCHING a resource might lead to creating a new resource as the copy of the PATCHed resource, with a different id. After the PATCH operation, the previous resource will not be available anymore.

I opted for using PATCH in this use case since PATCH is described as being not idempotent.
However, the resource identifier being changed makes me wonder if the operations should rather be of type POST. After all, a new resource MIGHT be created (depends on the server implementation of the backend and the underlying database).

So in some cases

PATCH /articles/1 HTTP/1.1

{
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "new title"
    }
  }
}

will respond with the following Resource Object

{
  "data": {
    "type": "articles",
    "id": "2",
    "attributes": {
      "title": "new title"
    }
  }
}

and a call to

GET /articles/1 HTTP/1.1

will result in a 404 since this resource was “moved” to /articles/2

What would be the best way to implement this use case considering the fact that some servers might create new resources when an attribute gets changed, and some don’t?

This sounds like a case of your API not being compliant with JSON:API. There’s a bit of an impedance mismatch; perhaps you’re using a database construct as your id value where it isn’t appropriate?

-Fred

Fred, thanks for your reply.
I’m currently in the process of converting the API to be as JSON:API compliant as possible. I will probably not meet 100% compliance but that’s okay if I can provide an API that meets well known specifications, even if it does not match them (I’m speaking mainly of request / response structure here).
The backend/services in question are POP/IMAP servers - the IMAP server’s uid is only unique per folder so I’m working with a compound key ({MailAccount}/{MailFolder}/{uid}). This compound key changes since IMAP does not support editing of messages, they have to be copied and will be stored under a new uid in the owning mail folder.
I’m wondering if generally switching to POST for the client edits would be the better idea since POST semantics reflect the IMAP operations better… What would be your thoughts on this?

EDIT:
The accounts used with the client are clearly marked as either IMAP or POP, so the client could decide whether to send a POST or a PATCH. The POST, however, would be in the need of a meta-object to reference the origin. Don’t see anything like this in the docs, so I guess POST does not allow for sending a meta object containing non-standard data?

If you know an edit will result in a new resource, you could define the API as a POST with only the attributes you’re changing, and then a relationship to the original version of the message. Edits that don’t create a new message can remain PATCH requests.

If you don’t know whether an edit is going to trigger the creation of a new resource, then you might need to do more research on your back-end IMAP service. But it sounds like that’s something you’ve got a handle on, since you’re deciding whether to make a copy.

If the IMAP service is your only back-end data store, this is hard to work around. If you had another database, you could assign ids there and update a reference to the actual IMAP uid in those records. But that means another datastore to keep in sync, and transactions become problematic.

-Fred

1 Like

Hey Fred, my thoughts exactly, thanks for the confirmation (see edit above).

I wanted to make sure the backend can operate by piping the calls directly through to the IMAP server, a SOA makes adding more complexity possible, but I wanted the original implementation to be as basic as possible.

There’s no reason meta can’t be provided with a POST, it’s just not often needed.

Glad we could come up with something to help this work out cleanly!

-Fred