Back to Blog
API performance
GraphQL
API integration
API versioning
API rate limiting

Best REST API Design Practices for 2025

4 min read
K
Kevin
API Security Specialist

Best REST API Design Practices for 2025

REST APIs continue to dominate as the backbone of modern web services, but best practices evolve with new technologies and standards.

1. Resource-Oriented Design

1.1 Nouns Over Verbs

Use nouns to represent resources rather than actions in your endpoints:

# Good
GET /users
GET /users/{id}

# Bad
GET /getUsers
GET /getUserById/{id}

1.2 Plural Resource Names

Consistently use plural nouns for collections:

GET /products
POST /products
GET /products/{id}

1.3 Nested Resources for Relationships

Express hierarchical relationships through path structure:

GET /users/{userId}/orders
POST /users/{userId}/orders
GET /users/{userId}/orders/{orderId}

2. Versioning Strategies

2.1 URL Versioning

Include the version in the URL path for clarity:

GET /v2/products

2.2 Header Versioning

Alternatively, use custom headers for version control:

GET /products
Accept-Version: 2.0

2.3 Semantic Versioning

Follow semantic versioning (SemVer) for API changes:

GET /v1.2.3/products

3. Hypermedia Controls (HATEOAS)

Implement HATEOAS to make your API self-descriptive:

{
  "id": 123,
  "name": "Premium Widget",
  "price": 99.99,
  "_links": {
    "self": { "href": "/products/123" },
    "reviews": { "href": "/products/123/reviews" },
    "purchase": { "href": "/orders", "method": "POST" }
  }
}

4. Error Handling

4.1 Standard HTTP Status Codes

Use appropriate HTTP status codes:

  • 200 OK - Successful GET requests
  • 201 Created - Resource created
  • 204 No Content - Successful DELETE
  • 400 Bad Request - Client error
  • 401 Unauthorized - Authentication needed
  • 404 Not Found - Resource doesn't exist
  • 429 Too Many Requests - Rate limiting
  • 500 Internal Server Error - Server failure

4.2 Detailed Error Responses

Provide structured error details:

{
  "error": {
    "code": "invalid_parameter",
    "message": "The 'price' parameter must be a positive number",
    "target": "price",
    "details": [
      {
        "code": "min_value",
        "message": "Value must be greater than 0"
      }
    ]
  }
}

5. Pagination and Filtering

5.1 Cursor-Based Pagination

Use cursors for efficient pagination:

GET /products?limit=20&after=MjAyMy0wMS0wMVQwMDowMDowMF9pZDEyMw==

Response includes pagination metadata:

{
  "data": [...],
  "pagination": {
    "next_cursor": "MjAyMy0wMS0wMVQwMDowMDowMF9pZDEyNA==",
    "has_more": true
  }
}

5.2 Advanced Filtering

Support complex filtering with a standardized syntax:

GET /products?filter=price>100&sort=-created_at,price&fields=id,name,price

6. Performance Optimization

6.1 Partial Responses

Allow clients to request only needed fields:

GET /users?fields=id,name,email

6.2 Compression

Enable response compression:

GET /products
Accept-Encoding: gzip, deflate, br

6.3 Conditional Requests

Support caching with ETags:

GET /products/123
If-None-Match: "686897696a7c876b7e"

7. Security Best Practices

7.1 Authentication

Use OAuth 2.1 (RFC 6749bis) with PKCE:

POST /oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=xyz
&client_id=client
&code_verifier=abc
&redirect_uri=https://app/callback

7.2 Rate Limiting

Implement rate limiting headers:

HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 997
X-RateLimit-Reset: 3600

7.3 Input Validation

Validate all inputs:

# Python example using Pydantic
from pydantic import BaseModel, constr, conint

class CreateUser(BaseModel):
    username: constr(min_length=3, max_length=20)
    email: EmailStr
    age: conint(gt=0, lt=120)

8. Documentation Standards

8.1 OpenAPI 3.1

Use OpenAPI 3.1 for machine-readable documentation:

openapi: 3.1.0
info:
  title: Product API
  version: 1.0.0
paths:
  /products:
    get:
      summary: List all products
      parameters:
        - $ref: '#/components/parameters/page'
      responses:
        '200':
          description: A list of products
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Products'

8.2 Interactive Documentation

Provide developer-friendly interfaces like Swagger UI or Redoc.

9. Webhooks and Async Operations

9.1 Webhook Implementation

Support event subscriptions:

POST /webhooks
Content-Type: application/json

{
  "event": "order.created",
  "callback_url": "https://client.example.com/notifications",
  "secret": "client-secret"
}

9.2 Async Operations

For long-running operations:

POST /imports
Content-Type: application/json

{
  "file_url": "https://example.com/data.csv"
}

Response:

HTTP/1.1 202 Accepted
Location: /operations/import-123
Retry-After: 30

10. GraphQL Compatibility Layer

Consider adding a GraphQL compatibility layer for flexibility:

type Query {
  products(filter: ProductFilter, page: Pagination): ProductConnection
}

type Product {
  id: ID!
  name: String!
  price: Float!
}

By following these practices, you'll create APIs that are scalable, maintainable, and developer-friendly in 2025's evolving technical landscape.

Back to Blog