Skip to content

Backend Development

Web API Standards Every Backend Developer Should Know

A working API is not necessarily a well-designed API. As applications grow and more clients consume the same backend, inconsistent endpoints, unpredictable responses, and missing security controls become increasingly expensive to maintain. Understanding common Web API standards helps developers build predictable, secure, and maintainable interfaces regardless of the backend framework.

A practical guide to Web API standards and conventions, covering RESTful endpoints, HTTP methods, response structures, authentication, pagination, versioning, documentation, security, and testing.

Back to blog

Web API Standards Every Backend Developer Should Know

Building a Web API is relatively straightforward.

You create an endpoint, receive a request, execute some business logic, and return a response.

But building an API that remains predictable, secure, and maintainable as an application grows requires more than working endpoints.

Consider an application where one endpoint returns an object directly, another wraps it inside data, and a third returns 200 OK even when an operation fails.

The frontend now needs different handling logic for operations that should behave consistently.

Introduce a mobile application, an external integration, or another backend service, and those inconsistencies become even more expensive.

This is why API standards and conventions matter.

They establish a shared contract between the backend and its consumers.

Whether you are building with Laravel, ASP.NET Core, NestJS, or another framework, understanding these practices can help you design APIs that are easier to consume, test, document, and maintain.

Here are the standards and conventions I consider when designing a production-ready Web API.


1. RESTful Resource Naming

One of the first decisions in API design is how to structure endpoints.

A common mistake is designing endpoints around controller methods instead of the resources they represent.

For example:

GET  /api/GetAllUsers
POST /api/CreateUser
POST /api/UpdateUser
POST /api/DeleteUser

These endpoints describe operations.

The problem is that HTTP already provides methods for expressing common resource operations.

A more consistent approach is to design endpoints around resources.

GET    /api/v1/users
GET    /api/v1/users/123
POST   /api/v1/users
PUT    /api/v1/users/123
PATCH  /api/v1/users/123
DELETE /api/v1/users/123

Here, /users identifies a collection, while /users/123 identifies an individual resource.

The HTTP method communicates the intended operation.

Common naming conventions

Use plural nouns for resource collections:

/api/v1/users
/api/v1/products
/api/v1/orders

Use lowercase path segments and a consistent naming convention for multiword resources:

/api/v1/order-items
/api/v1/payment-methods

Represent relationships using nested routes when the parent-child relationship is meaningful:

GET /api/v1/users/123/orders
GET /api/v1/orders/456/items

Avoid unnecessary nesting when the resource has its own unique identifier.

For example:

GET /api/v1/orders/456

is generally easier to consume than requiring every client to know the entire hierarchy:

GET /api/v1/users/123/orders/456

These are widely adopted REST conventions rather than mandatory URI syntax rules.

The important part is choosing a consistent structure.


2. Use HTTP Methods According to Their Intended Semantics

HTTP methods are not interchangeable.

Each method communicates something about the operation being performed.

Method Intended purpose
GET Retrieve a resource or collection
POST Submit data for processing or create a resource
PUT Create or replace the state of a resource at a target URI
PATCH Apply partial modifications to a resource
DELETE Remove a resource

For example, updating only a user's display name can be represented using PATCH:

PATCH /api/v1/users/123
Content-Type: application/json
{
  "name": "John Doe"
}

Avoid using GET for operations that intentionally modify application state.

GET /api/v1/users/123/delete

GET is defined as a safe HTTP method. Clients, caches, and automated tools may issue GET requests without expecting them to modify data.

Understand idempotency

An operation is idempotent when performing the same request multiple times has the same intended effect on server state as performing it once.

PUT and DELETE are idempotent by their defined HTTP semantics.

POST and PATCH are not guaranteed to be idempotent, although particular operations can be designed to behave that way.

This distinction becomes especially important when clients retry requests after network failures.

For operations such as payment creation, consider using an idempotency key so repeated submissions do not unintentionally create duplicate transactions.


3. Return Appropriate HTTP Status Codes

One of the most common API design mistakes is returning 200 OK for every request, including failures.

For example:

{
  "success": false,
  "message": "User not found"
}

If the HTTP response status is 200, the transport-level response indicates success even though the requested operation failed.

This makes error handling, monitoring, and client integrations less predictable.

Use HTTP status codes to communicate the outcome of the request.

Successful responses

Status Meaning
200 OK Request completed successfully
201 Created A resource was created
202 Accepted Request accepted for processing
204 No Content Successful request without a response body

A successful resource creation might return:

HTTP/1.1 201 Created
Location: /api/v1/users/123
Content-Type: application/json
{
  "data": {
    "id": 123,
    "name": "John Doe"
  }
}

A successful deletion may return:

HTTP/1.1 204 No Content

A 204 response must not include a response body.

Client and server errors

Status Meaning
400 Invalid request syntax or another client request error
401 Missing or invalid authentication credentials
403 Authenticated client lacks permission
404 Resource not found
409 Request conflicts with the current resource state
422 Request content is semantically invalid
429 Too many requests
500 Unexpected server error
503 Service temporarily unavailable

Use 401 for authentication failures and 403 for authorization failures.

For example, an unauthenticated user attempting to access a protected endpoint may receive 401, while an authenticated user without permission to modify a protected record may receive 403.

Do not expose internal exception details or stack traces in production responses.

The client needs enough information to understand and handle the failure, not access to your application's internal implementation.


4. Standardize JSON Response Structures

An API becomes difficult to consume when every endpoint returns a different response structure.

Consider these three responses:

{
  "id": 123,
  "name": "John Doe"
}
{
  "user": {
    "id": 123,
    "name": "John Doe"
  }
}
{
  "success": true,
  "result": {
    "id": 123,
    "name": "John Doe"
  }
}

All three can represent the same resource.

However, frontend applications must handle each structure differently.

A practical convention is to wrap successful resource responses inside a consistent data property.

{
  "data": {
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com",
    "created_at": "2026-09-24T06:00:00Z"
  }
}

This creates room for pagination metadata and additional information when needed.

For collections:

{
  "data": [
    {
      "id": 123,
      "name": "John Doe"
    },
    {
      "id": 124,
      "name": "Jane Doe"
    }
  ]
}

There is no universal REST requirement to use a data wrapper.

It is a convention that should be selected deliberately and applied consistently.

Standardize error responses with Problem Details

RFC 9457 defines a standardized format called Problem Details for HTTP APIs.

It provides a structured way to describe errors using the application/problem+json media type.

For example:

{
  "type": "https://api.example.com/problems/validation",
  "title": "Validation Failed",
  "status": 422,
  "detail": "The submitted data contains invalid fields.",
  "errors": {
    "email": [
      "The email field is required."
    ]
  }
}

The standard defines properties such as type, title, status, detail, and instance.

Additional properties, such as the errors object above, can be introduced for application-specific requirements.

This is particularly useful when multiple backend services need to expose errors using a common format.

For Laravel applications, API Resources can standardize successful response transformations, while exception handlers can normalize error responses.

ASP.NET Core also provides built-in Problem Details support.


5. Use Query Parameters for Filtering, Sorting, and Pagination

Avoid creating a separate endpoint for every possible variation of a resource collection.

For example:

GET /api/v1/GetActiveUsers
GET /api/v1/GetInactiveUsers
GET /api/v1/GetUsersByDate

A more flexible design uses query parameters.

Filtering

GET /api/v1/users?status=active

Multiple filters can be combined:

GET /api/v1/orders?status=pending&user_id=123

Sorting

GET /api/v1/users?sort=name

For descending order, a common convention uses a leading minus sign:

GET /api/v1/users?sort=-created_at

Pagination

GET /api/v1/users?page=2&per_page=20

A paginated response might look like this:

{
  "data": [
    {
      "id": 21,
      "name": "John Doe"
    }
  ],
  "meta": {
    "current_page": 2,
    "per_page": 20,
    "total": 100,
    "last_page": 5
  },
  "links": {
    "next": "/api/v1/users?page=3&per_page=20",
    "previous": "/api/v1/users?page=1&per_page=20"
  }
}

The specific parameter names are application conventions, not mandatory HTTP standards.

For large datasets or frequently changing collections, cursor-based pagination may be more appropriate than offset-based pagination.

Whatever strategy you choose, document it and keep it consistent across collection endpoints.

Also establish reasonable maximum page sizes and restrict filtering and sorting to explicitly supported fields.


6. Define an API Versioning Strategy

As an application evolves, its API contract may need to change.

Sometimes those changes are backward compatible.

Other times, they require existing API consumers to modify their implementations.

For example, removing a response field that mobile applications depend on can break older app versions.

API versioning helps manage these changes.

A common approach is URI-based versioning:

GET /api/v1/users
GET /api/v2/users

Other approaches include versioning through request headers or media types.

There is no universal requirement that every API must use URI-based versioning.

Choose the approach that suits your consumers and deployment requirements.

When should you introduce a new version?

Consider a new version when making breaking changes, such as:

  • Removing existing response fields.
  • Changing the meaning or type of existing fields.
  • Introducing incompatible request requirements.
  • Changing established endpoint behavior.

Not every internal refactor or backward-compatible feature addition requires a new API version.

The goal is to protect consumers from unexpected contract changes.

For public APIs, document supported versions and communicate deprecation timelines before retiring an existing version.


7. Separate Authentication From Authorization

Authentication and authorization are closely related, but they solve different problems.

Authentication determines who is making the request.

Authorization determines what that authenticated user or client is permitted to do.

For example, a valid authentication token should not automatically grant access to every user's records.

A request such as:

GET /api/v1/users/123/orders

must still verify whether the authenticated actor is permitted to access the requested orders.

Common authentication mechanisms

Session-based authentication is commonly used for first-party browser applications.

Bearer tokens are frequently used for mobile applications, service integrations, and API clients.

OAuth 2.0 provides a standardized framework for delegated authorization.

OpenID Connect extends OAuth 2.0 to support standardized user authentication.

Choose the mechanism based on your application's architecture and trust boundaries.

Enforce authorization on the backend

Never rely exclusively on hidden frontend buttons or client-side permission checks.

Even if a React component hides a delete button, a client can still attempt to call the API endpoint directly.

The backend must verify authorization for every protected operation.

In Laravel, Policies are a practical way to organize resource-level authorization.

In ASP.NET Core, authorization policies and resource-based authorization mechanisms provide similar capabilities.

Apply additional security controls

A production API should also enforce HTTPS, validate incoming data, restrict writable fields, apply appropriate rate limits, and avoid exposing sensitive details in error responses.

Authentication credentials should be stored securely and support appropriate expiration and revocation.

Security should be part of the API contract and implementation, not something added after the frontend is completed.


8. Document Your API With OpenAPI

An API becomes harder to maintain when its behavior exists only in the original developer's knowledge.

Frontend developers need to know which endpoints exist, what data they accept, what responses they return, and which authentication mechanisms they require.

Without documentation, developers often resort to inspecting backend source code or repeatedly asking how an endpoint works.

Use the OpenAPI Specification

OpenAPI provides a machine-readable format for describing HTTP APIs.

An OpenAPI document can describe:

  • Available endpoints and HTTP methods.
  • Query parameters and request bodies.
  • Response schemas and HTTP status codes.
  • Authentication requirements.
  • Validation constraints and supported data types.

For example:

openapi: 3.1.0

info:
  title: Example API
  version: 1.0.0

paths:
  /api/v1/users/{id}:
    get:
      summary: Retrieve a user

      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer

      responses:
        '200':
          description: User retrieved successfully

        '404':
          description: User not found

The documentation should reflect the behavior of the actual implementation.

Outdated documentation can be more harmful than missing documentation because consumers may depend on an incorrect contract.

Consider validating your API implementation against its documented schemas through automated contract tests.

OpenAPI can also support interactive API documentation and client generation.


9. Establish Consistent Data Conventions

Not every API design decision is covered by a mandatory industry standard.

However, consistent conventions are essential when multiple endpoints and services exchange data.

Field naming

Choose a convention such as snake_case:

{
  "first_name": "John",
  "created_at": "2026-09-24T06:00:00Z"
}

Or camelCase:

{
  "firstName": "John",
  "createdAt": "2026-09-24T06:00:00Z"
}

Both approaches are common.

The important part is to avoid changing conventions unpredictably between endpoints.

Date and time representation

Use standardized date and time representations, such as RFC 3339-compatible timestamps.

For example:

{
  "created_at": "2026-09-24T06:00:00Z"
}

Prefer UTC for storing and transmitting absolute timestamps unless the API has a specific reason to preserve an original offset.

For scheduling systems, distinguish between an absolute timestamp and a local date or time that depends on a particular time zone.

Resource identifiers

Use a consistent identifier strategy for your resources.

Integers, UUIDs, and ULIDs are common options.

Do not assume that using UUIDs automatically makes a resource secure.

Every protected resource still requires proper authorization.

Nullability and optional fields

Clearly define the difference between a missing field, a field explicitly set to null, and a field containing an empty value.

This matters especially for PATCH operations.

For example:

{
  "middle_name": null
}

may mean clearing the existing value, while omitting middle_name may mean leaving the current value unchanged.

These semantics should be documented so clients do not accidentally overwrite data.


10. Make Your API Observable and Testable

A well-designed API must remain understandable after deployment.

When a request fails, developers should be able to identify the affected endpoint, trace the request, and determine what happened without exposing sensitive information.

Request tracing

Assign or propagate a correlation or trace identifier for requests.

This allows logs from different application components to be connected to the same operation.

For systems with multiple services, distributed tracing becomes especially useful.

Health checks

Provide health-check endpoints suitable for your deployment environment.

For example:

GET /health/live
GET /health/ready

A liveness endpoint can indicate whether the application process is running.

A readiness endpoint can indicate whether the application is prepared to serve traffic.

These endpoints should not expose sensitive internal diagnostics publicly.

Automated testing

API tests should cover more than successful requests.

Verify that:

  • Unauthenticated requests are handled correctly.
  • Unauthorized users cannot access protected resources.
  • Invalid input returns the expected validation response.
  • Successful operations return appropriate status codes.
  • Pagination and filtering behave as documented.
  • Resource responses follow the established API contract.
  • Critical operations handle repeated requests safely.

In Laravel, feature tests can exercise the HTTP layer, authorization, validation, and database behavior together.

ASP.NET Core provides integration-testing capabilities for similar scenarios.

The objective is to test observable behavior rather than only checking that individual controller methods execute.


A Practical Web API Architecture

These standards become easier to maintain when the application itself has clearly defined responsibilities.

For a typical write operation, the flow might look like this:

HTTP Request
    ↓
Authentication
    ↓
Request Validation
    ↓
Controller / Endpoint
    ↓
Authorization
    ↓
Application Action
    ↓
Domain Logic / Models
    ↓
Database
    ↓
Response Transformation
    ↓
HTTP Response

The exact internal structure can vary between frameworks.

For example, Laravel applications may use Form Requests, Policies, focused Actions, Eloquent models, and API Resources.

ASP.NET Core applications may use endpoint filters or validation components, authorization policies, application services or handlers, EF Core, and response DTOs.

The architectural implementation is not required to be identical.

What matters is that the external API contract remains predictable.

A React frontend or Flutter mobile application should not need to understand whether the backend uses Laravel or ASP.NET Core to consume a well-defined API.

Authentication, endpoint naming, response formats, error handling, and pagination should remain consistent from the client's perspective.


Final Thoughts

A working API is only the beginning.

The real challenge is designing an interface that remains understandable as more features, consumers, and developers are introduced.

Following established standards helps reduce unnecessary integration work and prevents every endpoint from becoming its own special case.

Start with the fundamentals: resource-oriented routes, correct HTTP methods and status codes, predictable JSON responses, authorization, and validation.

Then establish conventions for pagination, versioning, documentation, observability, and testing.

You do not need to introduce every possible architectural abstraction before building an API.

You do need to make deliberate decisions about its contract.

A good API should be predictable enough that developers can understand how to use its next endpoint without having to relearn the entire system.

That consistency is what allows the application to grow while keeping integrations and maintenance manageable.


References