Skip to main content
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Ilya Fedotov
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Ilya Fedotov ( @ilyafedotov )

Why I Don't Like UPDATE Methods In API Design

By on

I am not an expert in API (Application Programming Interface) design. Full disclosure, I've never even built a publicly consumable API. I have built internal APIs that try to follow patterns that I've observed in the wild; but, my experience is certainly limited. That said, I do have some strong feelings about API design. Specifically, I am dubious of any API that allows for the generic "partial updating" of existing data. I believe that such API design lacks meaningful semantics and allows both creators and consumers of the API to think too much about "data persistence" and not enough about domain modeling.


 
 
 

 
API update ambiguity vs. semantic actions.  
 
 
 

In this conversation, I'm purposefully trying to avoid as many technical terms as I can. I don't want this to become a religious debate about the merits of REST or SOAP or if you should use JSON (JavaScript Object Notation) or XML or if you should provide embedded links for auto-discovery. This is intended to be more of a philosophical conversation about semantics and mental models.

That said, let me be clear about my reservations. Given an API that already has data for a Person X:

  • id: 4
  • name: Person X
  • age: 22
  • createdAt: 2016-04-04
  • updatedAt: 2016-08-01

... I try to avoid building an API that allows me to send generic UPDATE requests to change arbitrary field values:

UPDATE "Person X", Set "name" to "Person Xtraordinary"

To me, this kind of update lacks meaning. It lacks sematics. When you have a conversation about this kind of update, you have to include all of the data related to the update in order to ensure that all parties are on the same page about what exactly is being discussed. To me, this would be like talking about design patterns without giving design patterns names.

This kind of update also feels very ambiguous in terms of what is valid. Meaning, can I update the Person's "name" and "id" at the same time? Is it even legal to update the "id"? Do I have to provide the "updatedAt" field? Or, will that automatically update with the current timestamp? Can I set the "createdAt" date to be after the "updatedAt" date? Can I add a completely new field like "favoriteColor" when I send an update?

None of this is clear because UPDATE is not intentful. It doesn't really convey a specific desire on behalf of the consumer. I believe that requests of this nature should be wrapped up in meaningful "actions." Instead of "changing the name field", you're "renaming":

RENAME "Person X" to be "Person Xtraordinary"

Now, we can have a clear converstation - I'm talking about the "rename" action, not the "update action that happens to include a 'name' property and no other properties at the same time." It also becomes much more clear as to what data is valid. For example, in a "rename" action, it wouldn't make sense to also include the "age" field. Actions also help articulate the surface area of the API. They demarcate the set of meaningful requests that a consumer can make.

NOTE: I understand that documentation removes all ambiguity. But, I am talking about this from a mental model viewpoint, not from a "Read the manual" viewpoint.

Of course, not every request to an API can be wrapped up in a more semantic action. I'm not crazy! Sometimes, "update" is really the most meaningful thing you can do. But, I would suggest that this is perhaps rarer than you would think.

Viewing the API in terms of "actions" also helps prevent both the consumer and the provider from thinking too much in terms of data persistence. Admittedly, I have a lot of trouble articulating why I think this is a good thing, but my gut says it is.

I'm sure I haven't laid out the best argument. My reaction to "UPDATE" is definitely one rooted in "feelings" so it's hard to layout in a logic way. Perhaps the only thing I've truly convinced you of is my own buffoonery. At the end of the day, however, I believe that API application state is often changed through semantic actions; and, those actions can be clearly reflected in the API landscape.

Reader Comments

8 Comments

Let me first start by saying I agree with what you are saying, before I disagree with your approach. :)

So, philosophically you are right. A PATCH that does:
'UPDATE "Person X", Set "name" to "Person Xtraordinary"
is not such a great idea because of the complexity which may result from updating that single property. Even Roy Fielding himself admitted that PATCH was created because partial PUT is never RESTful. RESTful APIs were not originally designed to partially update content, especially with the way being done today, which is to send a PATCH with the resulting value of the key, like:

PATCH /users/X
{
"name": "Person Xtraordinary"
}

The PATCH verb though, does not restrict you in any way in how you send your updates. You could follow RFC 6902 (https://tools.ietf.org/html/rfc6902), which uses a JSON notation to specify a series of changes to be done:
PATCH /users/X
[
{ "op": "replace", "path": "/name", "value": "Person Xtraordinary" },
{ "op": "remove", "path": "/age" }
]
This allows you to have your 'actions' as you describe them above in any way you like them.

There is now however RFC 7396 (https://tools.ietf.org/html/rfc7396), which describes a way for you to send exactly what you need to update, and only what you need to update, so your example above would again become similar but now with a formal specification:
PATCH /users/X
Content-Type: application/merge-patch+json
{
"name": "Person Xtraordinary"
}

My real world experience in designing APIs tells me that when I have simple update rules with no adverse side effects, then it's OK to use the PATCH in its simplified form. It does its job fine. A huge hidden benefit of sending partial content is reduced bandwidth traffic between the consumer and the API but also between the API and the persistent layer. This actually satisfies most of the cases I have to deal with.

When things are more complicated in my persistent layer, like you described, then sticking with a pure PUT (replace) of the entire object may be more appropriate. But even in this case, you can very possibly easily create validation rules for your request payload, throwing 400's to cover your business rules.

Lastly, your comment:
---Viewing the API in terms of "actions" also helps prevent both the consumer and the provider from thinking too much in terms of data persistence"---
REST by definition is about Transfering around the State of a Representation (object), so it kind of forces you to think in those terms.

15,640 Comments

@Evagoras,

I think partial updates do have a place. There are times when simply updating an existing object is the most meaningful thing for the application. Like updating existing "Contact Information", for example. Maybe you just want to update the "street2" field or a add a secondary phone number.

Of course, depending on the application, maybe you can't simply update an address because it ties into other business rules around taxation or legal liabilities or what-have-you. In those cases, I agree with you that a more full-on PUT/replacement would make the most sense.

But, on this last thought, that is where I get concerned with the "data persistence" mentality. Imagine I have some important business object representation that has an "isActive" field. It's likely that moving the state of said object from "isActive:true" to "isActive:false" is a weighty action. Maybe there's validation; maybe emails have to be sent; maybe other logging objects have to be created; maybe other relationships have to be ended or deleted. Most likely, it's more than simply flipping a bit in a database row.

Now, at the end of the day, you could technically check for all that even if someone is just doing a partial-update for "isActive". But, to me, something about that just feels wrong. The consumer shouldn't feel like they are "flipping a bit" -- they should feel like they are "deactivating" something. Something with real meaning behind it.

8 Comments

@Ben,

I definitely agree that being fully RESTful can make someone lose context of what it is they are changing. I have run into scenarios where I had the same exact issues/concerns, and what I have done there is actually create "actions" endpoints.

Examples:
/memberships/1/actions/renew
/memberships/1/actions/cancel

Since REST is not a protocol but rather a style, and one that's based on best practices at that, I think creating such endpoints is perfectly valid.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel