REST isn’t a standard — it’s a set of architectural constraints. Understanding these constraints helps you make informed decisions about when to follow them and when to deviate.
The Six Constraints
- Client-Server — Separation of concerns
- Stateless — Each request contains all information needed
- Cacheable — Responses must define themselves as cacheable or not
- Uniform Interface — Consistent resource identification
- Layered System — Client shouldn’t know if it’s connected directly to the server
- Code on Demand (optional) — Server can extend client functionality
HATEOAS in Practice
Hypermedia as the engine of application state — linking related resources:
{
"data": {
"id": 42,
"type": "article",
"attributes": { "title": "REST Patterns" },
"links": {
"self": "/api/v1/articles/42",
"author": "/api/v1/users/7",
"comments": "/api/v1/articles/42/comments"
}
}
}
Content Negotiation
Use headers, not URL extensions:
GET /api/v1/articles HTTP/1.1
Accept: application/json
class ArticlesController < ApplicationController
def show
article = Article.find(params[:id])
respond_to do |format|
format.json { render json: article }
format.xml { render xml: article }
end
end
end
These patterns form the backbone of a well-designed REST API.