Designing RESTful APIs

1. Defining Resource-Based URLs

PatternExample
Collection/orders
Item/orders/{id}
Sub-resource/orders/{id}/lines
Action (when needed)/orders/{id}:cancel
Nouns, pluralLowercase, hyphens

2. Using HTTP Methods Correctly

MethodUseIdempotentSafe
GETRetrieveYesYes
POSTCreate / non-idempotentNoNo
PUTReplaceYesNo
PATCHPartial updateShould beNo
DELETERemoveYesNo
HEAD/OPTIONSMetadata/CORSYesYes

3. Implementing API Versioning

StyleExample
URL path/v1/orders
HeaderAPI-Version: 1
Accept media typeapplication/vnd.acme.v1+json
Query param?version=1

4. Implementing HATEOAS

Example: HAL response

{
  "id": "ord_123",
  "status": "PAID",
  "_links": {
    "self":   { "href": "/orders/ord_123" },
    "cancel": { "href": "/orders/ord_123:cancel", "method": "POST" },
    "lines":  { "href": "/orders/ord_123/lines" }
  }
}

5. Defining Request/Response Contracts

AspectConvention
JSON casecamelCase or snake_case (be consistent)
Date formatISO-8601 UTC
IDsString for opaque IDs
MoneyObject: { amount: 1000, currency: "USD" }
EnumUPPER_SNAKE_CASE strings

6. Handling Query Parameters

ParameterExample
Filter?status=PAID&customerId=123
Sort?sort=placedAt,desc
Pagination?page=0&size=20 or ?cursor=...&limit=20
Sparse fields?fields=id,total
Expand?include=customer,lines

7. Designing Error Responses

Example: RFC 7807 Problem Details

{
  "type":   "https://api.acme.com/errors/validation",
  "title":  "Validation failed",
  "status": 400,
  "detail": "Field 'currency' must be 3 letters",
  "instance": "/orders",
  "traceId": "abc-123",
  "errors": [
    { "field": "currency", "code": "pattern", "message": "must match ^[A-Z]{3}$" }
  ]
}

8. Implementing Content Negotiation

HeaderPurpose
AcceptClient wants this media type
Content-TypeDeclares request body type
Accept-LanguagePreferred locale
Accept-Encodinggzip/br compression

9. Designing Idempotent Operations

MethodIdempotent by Default?
GET/HEAD/PUT/DELETEYes
POSTNo → use Idempotency-Key header
PATCHDepends on body design

10. Using HTTP Status Codes Appropriately

CodeMeaning
200OK with body
201Created (include Location)
202Accepted (async)
204No Content
400Bad Request (client format)
401Unauthenticated
403Forbidden
404Not Found
409Conflict (version, dup)
422Unprocessable Entity (validation)
429Too Many Requests
500/502/503/504Server-side / gateway issues

11. Implementing Partial Responses

PatternDetail
Sparse fieldsets?fields=id,name,email
GraphQL-likeField selection in query
Server-side projectionMap at controller

12. Designing Bulk Operations Endpoints

Example: Bulk endpoint

POST /orders:batch
{ "items": [ { "customerId": "...", "lines": [...] }, ... ] }

200 OK
{ "results": [ { "status": "created", "id": "ord_1" },
               { "status": "error",   "error": { "code": "validation" } } ] }