Question

I design and works on lot of projects involves REST APIs. But one question is always occur to me if it is acceptable way to do REST.

So according to REST manuals online, REST is build upon two major HTTP concepts. HTTP Verbs and HTTP Codes ( their are other thing but I am focusing on these two concepts ).

So If I need to made User CRUD APIs, Then I would be making like that

Add -> POST /api/user
GET ALL -> GET /api/user
GET ONE -> GET /api/user/:id
UPDATE ONE -> PUT /api/user/:id
DELETE ONE -> DELETE /api/user/:id

Responses would have http code like that

Success : 200
Wrong Input Data Format: 400 
Authentication Error: 401
Authorization Error: 403
Validation Error: 422
Internal Server Error: 500

But these concepts not followed during API design in any of my projects.

So instead of above, concepts like below used.

Add -> POST /api/add_user
GET ALL -> GET /api/getusers
GET ONE -> GET /api/getuser?user_id=12345
UPDATE ONE -> POST /api/update_user
DELETE ONE -> POST /api/delete_user

With Responses not based upon HTTP code at all. But given HTTP Code 200 in all cases with custom code and message, Something like that

Success : HTTP 200
{
    status: true,
    errorCode: 0,
    message: "",
    data: {} 
}  

Failure : HTTP 200
{
    status: false,
    errorCode: 1233,
    message: "",
    data: {} 
}  

My question is that if bottom approach is acceptable according to minimum REST API Design principals. Also if First approach is overkill.

Was it helpful?

Solution

REST

REST is build upon two major HTTP concepts

Technically, this is backwards. REST is the architectural style, HTTP is part of the reference implementation (aka the world wide web).

What you are describing is a routing convention, not REST. For example: Rails Routing from the Outside In.

URI

Add -> POST /api/add_user
GET ALL -> GET /api/getusers
GET ONE -> GET /api/getuser?user_id=12345
UPDATE ONE -> POST /api/update_user
DELETE ONE -> POST /api/delete_user

REST doesn't care what spelling is used for identifiers. See this talk by Stefan Tilkov -- part of the point is that the server is supposed to be able to control its own identifier namespace.

GET /api/user/:id
GET /api/getuser?user_id=12345
GET /d93fb329-ca37-4bd2-8b76-ec6ec600e13d

These spellings are all fine.

REST cares about the spelling of identifiers in much the same way that compilers and interpreters care about the spelling of variable names: not very much. The human beings in the system may care which is why we have spelling conventions. But those spellings are aligned with local norms.

REST does care that two identifiers are the same; see below.

METHOD

Using GET for all safe requests, and POST for all unsafe requests, is fine. HTML, for example, does just fine with only GET and POST, and that's the backbone of the hypermedia world wide web.

PUT and DELETE offer more specific semantics, which can be a great convenience. But they aren't required.

You're also allowed to have more than one way to do it. For example, a resource might support both DELETE and also POST, providing a convenient endpoint for an HTML form.

CACHING

Caching of responses is one place where you begin to care whether two identifiers are the same or not.

GET ONE    -> GET /api/user/:id
UPDATE ONE -> PUT /api/user/:id

In this design, all of the generic HTTP aware components know that a successful PUT means that previously cached representations of /api/user/:id are invalid. We don't have to do anything explicit to make that happen, because the semantics are already defined in HTTP.

If we change PUT to POST, invalidation still works, because both are "unsafe request methods". So this is also fine:

GET ONE    -> GET  /api/user/:id
UPDATE ONE -> POST /api/user/:id

But in your second example, the spelling of the URI has been changed, so you lose the invalidation semantics

GET ONE    -> GET /api/getuser?user_id=12345
UPDATE ONE -> POST /api/update_user
DELETE ONE -> POST /api/delete_user

Here, a successful POST invalidates /api/update_user or /api/delete_user, where what you would normally want is that it invalidates /api/getuser?user_id=12345. If you were to instead use

GET ONE    -> GET  /api/getuser?user_id=12345
UPDATE ONE -> POST /api/getuser?user_id=12345
DELETE ONE -> POST /api/getuser?user_id=12345

Then that would be fine. There's nothing wrong with having two different forms that submit to the same URI -- you just need your API to be able to distinguish between the two.

GET ONE    -> GET    /api/getuser?user_id=12345
UPDATE ONE -> PUT    /api/getuser?user_id=12345
DELETE ONE -> DELETE /api/getuser?user_id=12345

This, of course, is also fine, and gives you the advantages of the generic PUT and DELETE semantics.

STATUS CODE

We care about status codes because those codes are meta data that allow generic components (like a cache) to know if a particular request was successful or not, which in turn instructs it as to whether or not previous representations should be invalidated.

If you don't lift that information from the message body into the headers, the intermediate components aren't going to see it.

Conclusion

I think it would help to review what David Webber has to say about HTTP:

HTTP is an application protocol whose application domain is the transfer of documents over a network.

Your REST API is an attempt to disguise your business capabilities as a document store. And if you get the small details right, then a lot of the work has already been done for you.

OTHER TIPS

Yes and no.

Yes, 200 is fine if your application code ran ok but its designed to return an integer code rather than throw an exception.

Yes, I think you are right to avoid using the http error codes where the error is thrown by your application, rather than a protocol error.

No, I would avoid having a Response Template where all your responses are wrapped in with meta data about whether the request was successful or not.

In summary

  • 200 : good, use as default
  • 401/403 : good auth is part of the protocol
  • anything else : bad use 500 with the Exception thrown serialised in the body.

eg.

Success : HTTP 200
{
    //json data returned by your function or blank
}

Failure : HTTP 500
{
    "Message" : "whatever",
    //additonal fields required for your exception class
}

This allows you to fully report errors to your client, whilst also clearly delimiting hosting and application errors in your logs

Licensed under: CC-BY-SA with attribution
scroll top