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:
GETfor ReadPOSTfor CreatePUTfor UpdateDELETEfor (guess what?) Delete
Suppose we want to provide some APIs to manage users. We may have APIs like the following:
GET /usersto get all usersPOST /usersto 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/sendandPOST /users/{userId}/messages/{id}/trashin gmail APIPOST /videos/ratein youtube APIPOST /v1/payments/sale/{sale_id}/refundin paypal API
Opinions expressed by DZone contributors are their own.
Comments