Sebastien Lorber
Sebastien Lorber

Reputation: 92112

Can't understand some basic REST stuff

Suppose my model is:

User:

I have a collection /users/

I want the Users to be retrieved by /users/{id} and not /users/${nickname}, because in some more complex cases, there could be no "logical unique constraint".

So the basic JSON payload I could use is for exemple:

{
  id: 123,
  nickname: 'someUserName'
}

Nothing fancy here.


POST on /users/

As far as I know, an user as an identifier. It is part of the resource representation, so it should be in the payload (?).

Put what if I want to generate the ID myself on the backend, using a DB sequence for exemple?

Then my payload becomes:

{
  nickname: 'someUserName'
}

Is this appropriate?

What is supposed to be the output of this POST? Nothing? Just a header referencing the resource location, including the ID?


GET on /users/id

When we get the resource, we load its content as JSON:

{
  id: 123,
  nickname: 'someUserName'
}

PUT on /users/id

As far as I know, the payload used on this method is supposed to "override" the resource content. If we wanted partial updates, we would have used PATCH.

But what if I do:

PUT /users/123

{
  id: 456,
  nickname: 'someUserName'
}

Does this mean that we want to update the id of a resource?

Isn't it kind of redundant to use the id in both the URI and the payload?


Actually I don't really know how to handle the id.

I don't know if I am supposed to use the same resource representation in all POST / PUT / DELETE operations.

I don't know if the id should be part of the unique(?) resource representation. But if the id is not part of the representation, then when I list the users, using GET /users/, if the ids are not returned, then I don't know how the client can get the user ids...

Can someone help me? :)

Upvotes: 2

Views: 323

Answers (2)

Samuel Herzog
Samuel Herzog

Reputation: 3611

First of all
It is not REST if you don't use HATEOAS

I hope you understand this, I'll come back to that at the very end.

POST on /users/

It perfectly ok to not use an ID in the POST payload. If an ID is present react with an error message, so developers understand they are doing wrong.
Therefore only the nickname as a payload is perfectly valid if you don't have anything else in your user resource

The output of your server should include three important things:

  1. HEADER: A status code indicating success or failure (usually 201 Created)
  2. HEADER: The location of the newly created resource (just Location: /path/to/resource)
  3. BODY: A representation of the created resource. Give back a complete payload like on a GET!

GET

perfectly valid

PUT

your analysis regarding PUT/PATCH matchs the spec, the new resource should be identical to the payload meaning the user wishes to change the id if it differs. if a payload contains values which shouldn't be changed (like the ID) you have two possibilities:

  1. Ignore the ID in the payload
  2. Return an error

In both cases inform the user about what you did and what went wrong. I prefer to send/get a 400 Bad Request. If a privileged user could change the ID but the particular user can't an 403 Forbidden may be more appropriate. Also make sure to document your APIs behaviour. You may allow the ID to be omitted in your API. Don't forget to treat IDs given in a POST payload in a consistent way!

Overall questions

REST operates over Resources.
/users/ is an example for an collection of resources
/users/{id} is an example for a single resource
You should always use the exact same representation in each and every response. If for some reason it is more appropriate to give only a snippet of the information add metadata (link) pointing to the full resource representation.
The ID is always present except in the first POST request of an user. POST implies that the future location of the resource is not known and has to be provided by the server. This also means that GET /users/ should return the IDs for each resource.

As always in APIs return strict and be forgiving in requests. document your behaviour so users can learn.

HATEOAS

The true beauty of REST comes to daylight if you implement HATEOAS (Hypermedia As The Engine Of Application State). Part of this means that you should sugar your representations with useful tag/link combinations. This way clients never have to construct an url anymore.

An Example using HAL for your user representation would be:

{
    "_links:" {
        "self": { "href": "http://yourrest/users/123" }
    },
    "id": "123"
    "nickname": "someUserName"
}

A nice wrapup of using HAL was written by Matthew Weier O'Phinney in his blog when he developed a ZF2 REST Module (first entry is completly zf free, only explaining HAL).

Upvotes: 5

Charles Engelke
Charles Engelke

Reputation: 5649

I'm interpreting your descriptions as saying that the id is not part of the resource, it's a unique identifier of the resource. In that case, it should not be part of the payload for any operation.

POST /users with payload {"nickname": "somebody"} would create a new resource with a URL returned in the Location header. That URL would presumably look like /users/123 but from the client's point of view there's no reason to expect that. It could look like /something/else/entirely.

GET /users/123 would (assuming that URL was returned by an earlier POST) return the payload {"nickname": "somebody"}.

PUT /users/123 would (with the same assumption as above) replace the resource with the payload you send with the PUT, say {"nickname": "somebody else"}.

If you want the client to be able to name a resource, then you'd also let PUT /users/123 create a new resource with that URL.

I know of no common RESTful way to rename a resource. I suppose a POST with the old URL as part of the query part or the body would make sense.

Now, suppose I'm wrong and you do want id to be part of the resource itself. Then every payload would include it. But from the client's point of view, there should be no assumption that "id": 123 implies that the URL would be /users/123.

Finally, all of this is from a fairly purist point of view. There is value to thinking of URLs as the only real identifier of a resource, but it's not awful to break that rule and have the client use logic to create the URLs.

Upvotes: 0

Related Questions