REST API Best Practices

Industry best practices and guidelines for building professional-grade REST APIs with .NET

This section outlines comprehensive best practices for designing, implementing, and maintaining REST APIs. Following these guidelines will help you create APIs that are intuitive, secure, maintainable, and aligned with industry standards.

Guide Overview

This main Best Practices guide provides general principles for REST API design, while specialized topics are covered in more detail in the following dedicated guides:

GuidePurpose
HTTP Methods: POST vs PUT vs PATCHUnderstand the differences between HTTP methods and when to use each
HTTP Status CodesComprehensive reference for selecting appropriate status codes
SecurityImplementing authentication, authorization, and other security measures
VersioningStrategies for evolving your API without breaking clients
ContractsDefining and maintaining API contracts

Core REST API Design Principles

Resource-Oriented Design

Design your API around resources (nouns) rather than actions (verbs). Resources should be identified by URIs, and actions on those resources should be expressed using appropriate HTTP methods.

Good:

  • GET /users - Get list of users
  • GET /users/123 - Get user with ID 123
  • POST /users - Create a new user
  • PUT /users/123 - Update user with ID 123

Avoid:

  • GET /getUsers
  • POST /createUser
  • PUT /updateUser/123
  • GET /listAllUsersInSystem

Use HTTP Methods Appropriately

Each HTTP method has specific semantics that should be respected:

MethodUse forIdempotentSafe
GETReading resourcesYesYes
POSTCreating resourcesNoNo
PUTReplacing resourcesYesNo
PATCHPartially updating resourcesNo*No
DELETERemoving resourcesYesNo
HEADChecking resource metadataYesYes
OPTIONSDiscovering supported methodsYesYes

*PATCH can be made idempotent depending on implementation

💡 Deep Dive: For detailed guidance on choosing between HTTP methods, including implementation examples and decision flowcharts, see the HTTP Methods: POST vs PUT vs PATCH guide.

Use Proper HTTP Status Codes

Communicate the result of operations through appropriate HTTP status codes:

  • 2xx: Success

    • 200 OK: Standard success response
    • 201 Created: Resource successfully created
    • 204 No Content: Success with no response body
  • 4xx: Client errors

    • 400 Bad Request: Invalid request format/syntax
    • 401 Unauthorized: Authentication required
    • 403 Forbidden: Authentication succeeded but insufficient permissions
    • 404 Not Found: Resource doesn’t exist
    • 422 Unprocessable Entity: Request format is valid but semantically incorrect
  • 5xx: Server errors

    • 500 Internal Server Error: General server error
    • 503 Service Unavailable: Server temporarily unavailable

💡 Deep Dive: For implementation examples, decision flowcharts, and anti-patterns to avoid, see our comprehensive HTTP Status Codes guide.

Consistent Naming Conventions

  • Use kebab-case for URIs: /api/order-items (preferred for URLs)
  • Use camelCase for query parameters: /api/users?sortBy=lastName
  • Use camelCase for JSON property names: { "firstName": "John" }
  • Use plural nouns for collections: /api/users instead of /api/user
  • Be consistent in your terminology across endpoints

Resource Design & Relationships

Handling Resources with Relationships

Express relationships between resources using nested paths for clarity and logical organization:

  • GET /users/123/orders - Get all orders for user 123
  • GET /orders/456/items - Get all items for order 456
  • POST /users/123/orders - Create a new order for user 123

For many-to-many relationships, consider whether the relationship itself is a resource:

  • PUT /articles/123/tags/456 - Associate tag 456 with article 123
  • DELETE /articles/123/tags/456 - Remove tag 456 from article 123

Collection Pagination

Always implement pagination for collection endpoints to avoid performance issues with large datasets:

GET /users?page=2&pageSize=50

Response should include pagination metadata:

{
  "items": [...],
  "page": 2,
  "pageSize": 50,
  "totalItems": 327,
  "totalPages": 7
}

Resource Filtering, Sorting, and Selection

  • Filtering: Use query parameters to filter collections

    • GET /users?status=active&role=admin
  • Sorting: Allow sorting by relevant fields

    • GET /users?sortBy=lastName&sortOrder=desc
  • Field Selection: Let clients request only needed fields

    • GET /users?fields=id,firstName,lastName,email

Request & Response Handling

Use JSON for Request and Response Bodies

JSON has become the standard format for API payloads due to its simplicity, readability, and widespread support.

Consistent Error Responses

Standardize your error response format for better client experience:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request parameters",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address"
      },
      {
        "field": "password",
        "message": "Must be at least 8 characters"
      }
    ]
  }
}

Content Negotiation

Support content negotiation using the Accept header to allow clients to specify the response format:

Accept: application/json
Accept: application/xml
Accept: application/json; version=2.0

Validation

  • Validate all input data and return descriptive validation errors
  • Use consistent validation patterns across all endpoints
  • Return validation errors with status code 400 Bad Request
  • Include enough detail for clients to fix the issue

Security Considerations

Security is a critical aspect of any REST API implementation. Some key principles include:

  • Always use HTTPS for all API endpoints
  • Implement proper authentication and authorization
  • Validate all input data to prevent injection attacks
  • Use proper HTTP security headers
  • Follow the principle of least privilege
  • Implement rate limiting to prevent abuse

💡 Deep Dive: The Security Best Practices guide provides comprehensive coverage of:

  • Authentication methods comparison (API keys, JWT, OAuth)
  • ASP.NET Core implementation examples
  • HTTPS configuration best practices
  • Protection against common attacks (CSRF, XSS, CORS)
  • Security headers and their implementation

API Evolution and Versioning

APIs evolve over time, and proper versioning is essential to maintain backward compatibility. Key versioning principles include:

  • Plan for changes from the start
  • Choose a consistent versioning strategy
  • Avoid breaking changes whenever possible
  • Provide clear documentation for each version
  • Implement a deprecation policy with appropriate notice periods

💡 Deep Dive: Our API Versioning guide covers:

  • Comprehensive comparison of versioning approaches (URL, header, query parameter)
  • ASP.NET Core implementation examples
  • API gateway configuration for version management (including Azure Front Door)
  • Best practices for backward compatibility
  • Strategies for managing API lifecycle and deprecation

Performance Optimization

Caching

Implement HTTP caching to improve performance and reduce server load:

  • Use ETag and Last-Modified headers
  • Set appropriate Cache-Control directives
  • Implement conditional requests with If-None-Match and If-Modified-Since

Example ASP.NET Core implementation:

app.UseResponseCaching();

[HttpGet]
[ResponseCache(Duration = 60, VaryByQueryKeys = new[] { "id" })]
public IActionResult GetResource(string id)
{
    // Implementation
}

Compression

Enable compression for API responses to reduce bandwidth usage:

app.UseResponseCompression();

Asynchronous Processing

For long-running operations, use asynchronous processing patterns:

  1. Accept the request and return 202 Accepted
  2. Create a status resource that clients can poll
  3. Optionally implement webhooks for push notifications

Example flow:

POST /orders
→ 202 Accepted
← Location: /order-processing/abc123

GET /order-processing/abc123
→ 200 OK
← { "status": "processing", "progress": 60 }

// Later...
GET /order-processing/abc123
→ 303 See Other
← Location: /orders/456

Documentation

OpenAPI/Swagger Specification

Document your API using OpenAPI (formerly Swagger) to provide:

  • Comprehensive API reference
  • Interactive documentation
  • Client code generation
  • Server stub generation
  • Contract testing capability

💡 Deep Dive: For a detailed comparison of contract types, implementation guidance, and selection criteria, see our API Contracts guide.

Include Examples

Always include request and response examples in your documentation:

// Request example
POST /users
{
  "firstName": "John",
  "lastName": "Doe",
  "email": "[email protected]"
}

// Response example - 201 Created
{
  "id": "123",
  "firstName": "John",
  "lastName": "Doe",
  "email": "[email protected]",
  "createdAt": "2025-05-10T12:00:00Z"
}

Monitoring and Observability

Implement Comprehensive Logging

Log API access and errors for troubleshooting and security monitoring:

  • Request details (method, path, query parameters, client IP)
  • Authentication information (user ID, client ID)
  • Response details (status code, response time)
  • Error details for failed requests

Health Checks

Provide health check endpoints to facilitate monitoring:

  • GET /health - Basic health check
  • GET /health/detailed - Comprehensive health status (protected)

Metrics Collection

Track metrics to understand API usage and performance:

  • Request rates and response times
  • Error rates and types
  • Resource utilization
  • Authentication failures
  • Rate limit usage

Implementation-Specific Best Practices

ASP.NET Core Implementation

For ASP.NET Core REST API implementations:

  • Use attribute routing for clear endpoint definitions
  • Implement model validation with data annotations
  • Use action filters for cross-cutting concerns
  • Leverage middleware for global behaviors
  • Implement proper dependency injection
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly IUserService _userService;

    public UsersController(IUserService userService)
    {
        _userService = userService;
    }

    [HttpGet]
    [ProducesResponseType(typeof(IEnumerable<UserDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetUsers([FromQuery] UserFilterModel filter)
    {
        var users = await _userService.GetUsersAsync(filter);
        return Ok(users);
    }

    [HttpGet("{id}")]
    [ProducesResponseType(typeof(UserDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetUser(string id)
    {
        var user = await _userService.GetUserByIdAsync(id);
        if (user == null)
            return NotFound();

        return Ok(user);
    }

    [HttpPost]
    [ProducesResponseType(typeof(UserDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> CreateUser([FromBody] CreateUserModel model)
    {
        var user = await _userService.CreateUserAsync(model);
        return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
    }
}

Testing Best Practices

Implement Comprehensive Testing

  • Unit tests for business logic
  • Integration tests for API endpoints
  • Contract tests to ensure specification conformance
  • Load tests for performance validation
  • Security tests for vulnerability assessment

Use Proper Test Isolation

  • Use in-memory databases for integration tests
  • Mock external services
  • Reset state between tests
  • Control test environment variables

Reference to Detailed Best Practice Guides

For more specialized guidance on specific aspects of REST API development, please refer to these dedicated sections:

TopicDescriptionLink
HTTP MethodsDetailed comparison of POST vs PUT vs PATCH with examples and decision flowchartsHTTP Methods: POST vs PUT vs PATCH
HTTP Status CodesComprehensive reference for status codes with implementation examplesHTTP Status Codes
API SecurityAuthentication, authorization, and protection against security threatsSecurity
API VersioningStrategies for versioning APIs including API gateway approachesVersioning
API ContractsContract types, pros and cons, and selection guidanceContracts

Summary

Creating effective REST APIs requires thoughtful design, careful implementation, and ongoing maintenance. By following these best practices, you’ll deliver APIs that are:

  • Intuitive for developers to understand and use
  • Consistent across endpoints and behaviors
  • Secure against common threats and vulnerabilities
  • Efficient in terms of performance and resource usage
  • Maintainable as your system evolves
  • Well-documented for ease of adoption

API Versioning Strategies

Guidelines for effectively versioning REST APIs in .NET applications to maintain backward compatibility

Securing REST APIs

Best practices for securing REST APIs in .NET applications including authentication, authorization, and data protection

Understanding HTTP Status Codes in REST APIs

Comprehensive guide to HTTP status codes and their appropriate usage in REST APIs

Choosing the Right HTTP Method for REST APIs

Detailed comparison of HTTP methods POST, PUT and PATCH and their proper usage in REST APIs