An Analysis on Designing Action-Based REST APIs
Designing action-based REST APIs for CRUD operations has a simple solution, whereas designing for non-CRUD operations is not as simple.
Join the DZone community and get the full member experience.
Join For FreeSOAP is based on operations that are mapped easily to a normal function in code. REST, on the other hand, deals with resources that should be mapped somehow to functions.
Code | SOAP | REST
getUsers() | getUsers | !
addUser(..) | addUser | !
UpdateUser(..) | updateUser | !
deleteUser(..) | deleteUser | !
REST is not a protocol itself; it depends only on what HTTP provides. Therefore, HTTP should provide us with a solution to do/ask for something.
CRUD Actions
Luckily, we have functions in HTTP request standard called HTTP methods, and it fits in normal CRUD operations:
GET
for ReadPOST
for CreatePUT
for UpdateDELETE
for (guess what?) Delete
Suppose we want to provide some APIs to manage users. We may have APIs like the following:
GET /users
to get all usersPOST /users
to add a new user (user details in the body)PUT /users/{userId}
to update a specific user (user details in the body)DELETE /users/{userId}
to delete a user
Non-CRUD Actions
HTTP is sufficient to that point, but what if we want to provide an API for activating or deactivating users? Unfortunately, HTTP doesn't have ACTIVATE and DEACTIVATE methods. Here's what we can do:
1. Expose a Property
In this way we may expose a property like status (even if you don't have such a property):
PUT /users/{userId}/status
ACTIVE
We may call this API passing a value in the body active/inactive to activate or deactivate the user.
2. Use PATCH
Unlike PUT
which does a full update/replace, HTTP has another method called PATCH
which does a partial update to one or more properties.
Even if you don't have a property for your action, your API may act like it has such one.
By having this API, you may send a partial update to only the status property
PATCH /users/{userId}
{
"status": "active"
}
3. Think About a Sub-Resource
We may invent a resource that handles this operation.
In our example, we have a user's sub-resource called active and we can locate it using the path /users/{userId}/active
and provide API for each action:
POST /users/{userId}/active
to activate the user
DELETE /users/{userId}/active
to deactivate the user
Pure Action
There are some actions that are not related to a property in an object or not persisted to a database, for example, a service that calculates something.
Another example is a function that calculates the square root of a number, rather than designing it like this:
POST /calculateSquareRoot?number={number}
It can be designed like this:
GET /square-roots/number
Note that also we converted it to a GET as the result is not changing, so it'd be a good idea to make it idempotent and safe, so it may be cached.
4. A Verb in the URI
One of the rules of REST is to depend on resources, So you shouldn't put verbs in your URIs.
Rules are made to be broken, choosing a verb may be more simple and better than other solutions.
Look at this:
POST /users/{userId}/activate
to activate the user
POST /users/{userId}/deactivate
to deactivate the user
For example:
POST /users/{userId}/drafts/send
andPOST /users/{userId}/messages/{id}/trash
in gmail APIPOST /videos/rate
in youtube APIPOST /v1/payments/sale/{sale_id}/refund
in paypal API
Opinions expressed by DZone contributors are their own.
Comments