Community RESTful HATEOAS Best Practices
Comprehensive guide to building REST APIs that reach the Glory of REST (Richardson Maturity Level 3) in Ruby on Rails. Contains 47 rules across 9 categories, ordered by the request/response lifecycle — from resource URI design through hypermedia link relations to API evolution.
When to Apply
Reference these guidelines when:
-
Designing new REST API endpoints and resource URIs
-
Adding hypermedia controls (_links, affordances) to API responses
-
Implementing content negotiation with HAL, JSON:API, or vendor media types
-
Building paginated, filterable, sortable collection endpoints
-
Reviewing APIs for proper HTTP method semantics and status codes
-
Evolving APIs without breaking existing clients
Rule Categories by Priority
Priority Category Impact Prefix
1 Resource Modeling CRITICAL res-
2 HTTP Method Semantics CRITICAL http-
3 Hypermedia & Link Relations CRITICAL link-
4 Status Codes & Response Headers HIGH status-
5 Content Negotiation & Media Types HIGH media-
6 Collection Patterns MEDIUM-HIGH coll-
7 Error Semantics MEDIUM err-
8 Caching & Conditional Requests MEDIUM cache-
9 API Evolution LOW-MEDIUM evolve-
Quick Reference
- Resource Modeling (CRITICAL)
-
res-noun-based-uris
-
URIs must be nouns, not verbs
-
res-plural-collection-uris
-
Always use plural nouns for collections
-
res-limit-nesting-depth
-
Limit nested resources to max 2 levels
-
res-model-business-entities
-
Model business entities, not database tables
-
res-use-consistent-identifiers
-
Use opaque identifiers, never auto-increment IDs
-
res-sub-resources-for-relationships
-
Express relationships as sub-resources
- HTTP Method Semantics (CRITICAL)
-
http-get-must-be-safe
-
Keep GET requests free of side effects
-
http-post-for-creation
-
Return 201 Created with Location header from POST
-
http-put-for-full-replacement
-
Use PUT only for full resource replacement
-
http-patch-for-partial-updates
-
PATCH for partial updates with merge semantics
-
http-delete-is-idempotent
-
Ensure DELETE is idempotent
-
http-head-for-metadata
-
Use HEAD for metadata without body transfer
-
http-idempotency-key
-
Use idempotency keys for safe POST retries
- Hypermedia & Link Relations (CRITICAL)
-
link-self-link-every-resource
-
Include a self link in every resource
-
link-related-resource-links
-
Link to related resources instead of foreign keys
-
link-action-affordances
-
Expose available actions as conditional links
-
link-standard-relation-types
-
Use IANA-registered link relation types
-
link-entry-point
-
Provide a root API entry point
-
link-pagination-links
-
Use hypermedia links for pagination
-
link-embedded-vs-linked
-
Choose between embedding and linking
- Status Codes & Response Headers (HIGH)
-
status-201-with-location
-
Return 201 Created with Location header
-
status-204-for-no-content
-
Return 204 No Content for empty responses
-
status-409-for-conflicts
-
Return 409 Conflict for state conflicts
-
status-202-for-async
-
Return 202 Accepted for async operations
-
status-allow-header-on-405
-
Return 405 with Allow header for wrong methods
-
status-rate-limit-headers
-
Include rate limit headers in API responses
- Content Negotiation & Media Types (HIGH)
-
media-accept-header-negotiation
-
Respect the Accept header for content negotiation
-
media-content-type-in-responses
-
Set the correct Content-Type in every response
-
media-vendor-media-types
-
Use vendor media types for API versioning
-
media-406-for-unsupported-types
-
Return 406 for unsupported media types
- Collection Patterns (MEDIUM-HIGH)
-
coll-cursor-pagination
-
Use cursor-based pagination instead of offset
-
coll-link-header-pagination
-
Include pagination links in body and Link header
-
coll-filtering-via-query-params
-
Support filtering via typed query parameters
-
coll-sorting-convention
-
Support sorting with a standardized sort parameter
-
coll-field-selection
-
Support sparse fieldsets via fields parameter
- Error Semantics (MEDIUM)
-
err-problem-details
-
Use Problem Details (RFC 9457) for errors
-
err-validation-errors
-
Return structured validation errors
-
err-error-links
-
Include recovery links in error responses
-
err-machine-readable-codes
-
Use machine-readable error codes
-
err-auth-error-codes
-
Distinguish 401 Unauthorized from 403 Forbidden
- Caching & Conditional Requests (MEDIUM)
-
cache-etag-conditional-get
-
Use ETags with stale? for conditional GET
-
cache-last-modified
-
Set Last-Modified for time-based validation
-
cache-cache-control-headers
-
Set explicit Cache-Control headers
-
cache-vary-header
-
Include Vary header for content-dependent caching
- API Evolution (LOW-MEDIUM)
-
evolve-additive-changes-only
-
Make only additive changes to responses
-
evolve-deprecation-headers
-
Use Deprecation and Sunset headers
-
evolve-hateoas-reduces-versioning
-
Leverage HATEOAS to eliminate URL versioning
How to Use
Read individual reference files for detailed explanations and code examples:
-
Section definitions - Category structure and impact levels
-
Rule template - Template for adding new rules
Reference Files
File Description
references/_sections.md Category definitions and ordering
assets/templates/_template.md Template for new rules
metadata.json Version and reference information