1

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:

Community
  • 1
  • 1
Alexander
  • 2,367
  • 1
  • 22
  • 28

4 Answers4

7

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.

cassiomolin
  • 101,346
  • 24
  • 214
  • 283
  • 1
    I like your answer (voted) as it gives me a better understanding of the REST approach. Yet I don't think simply changing a 'status' attribute would be acceptable in my case, since 'GET /resources' should not return the deactivated ones. – Alexander Apr 12 '17 at 08:10
  • 1
    @Alexander To get all resources you can request `GET /api/resources`. It will return the active and inactive ones. To filter the `resources` collection, you can use a query parameter like `status`. So `GET /api/resources?status=active` would return the active ones, while `GET /api/resources?status=inactive` would return the inactive ones. You could assume default values for your query parameters. If the `status` is ommited, assume that you want only the active ones. – cassiomolin Apr 12 '17 at 08:30
  • I disagree. 'GET /resources' should return all the objects, regardless of values of specific attributes, otherwise it is very confusing. But on the other hand, it should not return the 'deactivated' ones, which means it is not simply a regular attribute. – Alexander Apr 12 '17 at 08:40
  • 1
    @Alexander Have a look at the GitHub API to [list issues](https://developer.github.com/v3/issues/#list-issues): `GET /issues`. The `filter` query parameter can be used to indicates which sorts of issues to return. If no value is provided, the `assigned` value is used as default. – cassiomolin Apr 12 '17 at 09:02
  • Thanks Cassio. Well, convinced. They are placing a star on a gist with 'PUT' as well. https://developer.github.com/v3/gists/#star-a-gist . – Alexander Apr 12 '17 at 09:56
  • @cassiomolin - Ironically, given that this question is about avoiding verbs in REST URLs, the GiHub API you referenced actually uses verbs in their URLs in some cases. See https://developer.github.com/v3/issues/#lock-an-issue. You'll find this in google's BigQuery API's as well https://cloud.google.com/bigquery/docs/reference/rest/, refer to insertAll. The only thing I've found consistent about well known REST APIs is that overall, they're consistently inconsistent. Sometimes, just making things intuitive is the best approach, even if you're not "pure" by REST standards. – dcp Nov 11 '19 at 18:20
  • What would you recommend to use when returning a new instance of a resource? What I mean with new is that I want to have a GET endpoint to call for getting a new instance of some resource with default values set. This new resource is to be displayed in a form and the user will add data to it, and finally a POST will be posted. I cannot come up with a noun for that; only verb (create) or adjective (new). – andre Nov 22 '19 at 20:54
3

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.

Eric Stein
  • 11,713
  • 2
  • 31
  • 49
1

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

Community
  • 1
  • 1
Alexander
  • 2,367
  • 1
  • 22
  • 28
1

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.

mangonights
  • 709
  • 1
  • 7
  • 10