1
votes

I want to add actions to my REST API that would move 'resources' between different 'stores'.

For instance, suppose my resources are normally accessed by the following URL:

/resources
/resources/{resourceId}

Now suppose I want to 'deactivate' some resource, i.e. conceptually move it to another sub-folder. The most straightforward way to allow this would be as followed.

  1. 'Deactivate' the resource, i.e. cause it to be unavailable under /resources. Conceptually, it 'moves' the object to the '/resources/deactivated/' subfolder:

    POST /resources/{resourceId}/deactivate   
    

    Or alternatively:

    POST /resources/deactivated/{resourceId}
    
  2. Get all the deactivated objects:

    GET /resources/deactivated      
    
  3. Reverse the 'deactivate' action, i.e. conceptually move the object from the '/resources/deactivated/' subfolder back to the main one ('/resources').

    Either

    POST /resources/{resourceId}/reactivate    
    

    Or

    POST /resources/deactivated/{resourceId}/restore     
    

    This API seems rather intuitive for me. But it seems to violate the 'prefer nouns' rules that I have seen in many best practices-articles on REST API: I use verbs and adjectives instead of nouns!

Note that I might have parameters for all the endpoints, e.g. GET /resources/deactivated?createdBefore=01022017

Are there any better alternatives for my REST API? I.e. more RESTful, but not less intuitive ones?

Good resources that I could find on the topic:

4

4 Answers

7
votes

First of all, remember that REST stands for Representational State Transfer.

It is all about resources and their state. Operations such as activate, deactivate and move are all about replacing the current state of the resource with a new representation and you don't need verbs in the URL to express such operations.


For example, to replace a status of a resource, you can send a new representation of the resource in the payload of a PUT request:

PUT /api/resources/[id]/status HTTP/1.1
Host: example.org
Content-Type: application/json

{ "status" : "active" }

It can be understood as replace the status of the resource identified by [id] with the one sent in the request payload.


Then you could have the following to get the resources with a particular status:

GET /api/resources?status=active HTTP/1.1
Host: example.org
Accept: application/json

It can be understood as give me a representation of all resources with the status active.


To move a resource to another folder, for example, you could have:

PUT /api/resources/[id]/folder HTTP/1.1
Host: example.org
Content-Type: application/json

{ "target" : "draft" }

It can be understood as replace the folder of the resource identified by [id] with the one sent in the request payload.

3
votes

Is an active resource really that different than a deactivated resource? Consider just having a property that tracks activeness. You can always filter them out, such as

GET /things?active=true

You can alter just that property with a microPUT

PUT /things/{id}/active
false

If a thing and a deactivated-thing are conceptually different, it's reasonable to have two separate endpoints. I would move between them using

POST `/deactivated-things`
{
    "thing": "/things/12"
}

and

POST `/things`
{
    "deactivated-thing": "/deactivated-things/12"
}

You should try to avoid a path having multiple meanings. For example, don't do this:

/resources/{id}
/resources/deactivated/{id}

Don't overload the meaning of the path segment after /resources.

1
votes

Thanks Cassio for emphasizing the 'changing the object state' approach.

My own answer for completeness:

PATCH /resources/{resourceId} with body {"active":false}  -- deactivate a resource
PATCH /resources/{resourceId} with body {"active":true}  -- restore a resource
GET    /resources                        -- return all 'normal' resources
GET    /resources?includeInactive=true   -- return all resources including the deactivated ones
GET    /resources/{resourceId}           -- return the resource 

(The resources retrieved by 'GET' will contain the attribute 'active=true/false').

Seems like a classic case for PATCH: REST API PATCH or PUT

1
votes

One thing you never see mentioned with REST:

Don't confuse REST with web app or pretty urls.

eg. when a user logs in to edit their account, you would show

www.example.com/home
www.example.com/account
www.example.com/profile

and not

// www.example.com/users/{id} ...
www.example.com/users/433563444335634443356344
www.example.com/users/433563444335634443356344/edit

I think this is where many devs get confused. REST is great for APIs, but in terms of web apps, it should be used as the internal API for form action endpoints or ajax, for example, but not necessarily as the pretty url.