Skip to main content


Endpoints should be based on resources, not on actions. The request method determines the action.

Use nouns (not verbs) in the path, since the request method already has the verb.

  • Good:
  • Bad:

Limit levels/nesting to maximum collection/<id>/collection.

  • Good:
  • Bad:

Response formats


CRUD Routes

MethodRouteActionHTMLDescriptionSuccess CodeFailure CodeRequest BodyResponse Body
GET/usersindexList all users200 OK404 Not FoundResource list
GET/users/:idshowGet single user200 OK404 Not FoundResource
GET/users/newnewRender create form200 OK404 Not FoundHTML
POST/userscreateCreate new user201 Created400 Bad Request or 422 Unprocessable Entity if malformed, 409 Conflict if duplicateResourceLocation header + status - see below
GET/users/:id/editeditRender edit form200 OK404 Not FoundHTML
PUT/users/:idupdateUpdate user, or create if it doesn't exist200 OK or 204 No Content if updated, otherwise 201 CreatedResourceOptional
PATCH/users/:idupdateUpdate user, partial200 OK or 204 No Content404 Not FoundResourceOptional
DELETE/users/:iddeleteDelete user200 OK or 204 No ContentEntity describing status or nothing


  • Use 204 No Content if the response has no body, otherwise 200 OK.
  • 202 Accepted can be used if the operation is accepted but action is deferred.


Request has no body, response does.


Response should have "a Location header field that provides an identifier for the primary resource created and a representation that describes the status of the request while referring to the new resource(s)".


Also see:

If the request is malformed (eg a missing parameter) you can use either 400 Bad Request or 422 Unprocessable Entity. See:

POST if the resource already exists (eg duplicate username or email)

409 Conflict


POST to a collection, PUT to a resource source

PUT creates a resource if it doesn't exist, or updates it if it does (UPSERT).


Return 404 if the resource does not exist.

According to it should support UPSERT.


CRUD Examples

Fake API


the RPC model makes it very simple and direct for programmers to write a procedure in one program and call it from another. This is one of the characteristics that makes RPC so popular, but it also makes it easy for technology and use-case assumptions to flow easily from one application to the other, thereby coupling the two and making the system brittle.