Skip to content

Validating Inputs in a REST API

  • Input validation is the process of verifying that client-provided data meets expected formats, types, and constraints before processing it.
  • Without validation, REST APIs are vulnerable to security threats (e.g., SQL injection, command injection), broken functionality, and unexpected behavior from malformed data.
    • Example:
      • Valid: {"user_id": 123, "name": "Quacker"}
      • Invalid: {"user_id": "abc", "name": "##123@@"}
  • Proper validation ensures data integrity, reliability, and a better user experience by providing clear error messages when inputs are invalid.

For a REST API, inputs can be provided by the client request in the following ways:

  • Query string parameters (e.g., GET /species?limit=10)
  • URL path parameters (e.g., GET /players/123)
  • Request body (e.g., JSON in a POST, PUT, or PATCH request)
  • Request headers (e.g., Accept, Authorization)

When processing user or client-provided data in a REST API, you’ll need to validate:

  • Presence: Is a required field provided?
  • Type: Is the input an integer, string, boolean, etc.?
  • Format: Does it match a pattern (e.g., UUID for resource IDs, ISO 8601 for timestamps, valid enum values for status fields)?
  • Range/Length: Is it within acceptable bounds (e.g., pagination limit 1-100, offset >= 0, price > 0)?
  • Business rules: Does it make sense in your domain (e.g., stock quantity available, valid status transitions)?

There are two main approaches to validating inputs in PHP:

  1. Native PHP functions:
    • filter_var() for sanitizing and validating common types (e.g., email, URL).
    • isset(), empty() for checking presence.
    • Type casting/checking with is_int(), is_string(), etc.
    • Example:
      $email = 'test@example.com';
      if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
      echo 'Invalid email format';
      }
  2. Third-party libraries: For more complex validation, libraries like Valicomb provide built-in rules (e.g., required, email, min, max) and reduce boilerplate compared to writing manual checks. Valicomb is a simple, stand-alone validation library with no dependencies.

Resources:


  • Scenario: POST /players to create a player.
  • Validation Rules:
    • name: String, 2-50 chars, required.
    • age: Integer, 18-110, optional.
  • When validation fails, return a 422 Unprocessable Entity status with a structured JSON error response. Be specific in error messages, but avoid leaking internal details.
  • See also: Responding to Invalid Input.
Route callback in a Slim-based controller
public function handleCreatePlayer(Request $request, Response $response): Response
{
$data = $request->getParsedBody();
$errors = [];
// Validate name
if (!isset($data['name']) || empty($data['name'])) {
$errors[] = ['field' => 'name', 'message' => 'name is required'];
} elseif (strlen($data['name']) < 2 || strlen($data['name']) > 50) {
$errors[] = ['field' => 'name', 'message' => 'name must be between 2 and 50 characters'];
}
// Validate age (optional)
if (isset($data['age']) && (!is_numeric($data['age']) || $data['age'] < 18 || $data['age'] > 110)) {
$errors[] = ['field' => 'age', 'message' => 'age must be an integer between 18 and 110'];
}
if (!empty($errors)) {
return $this->renderJson($response, [
'status' => 'error',
'message' => 'Validation failed',
'details' => $errors
], 422);
}
// Validation passed: proceed with creating the player...
}

  1. Validate early: Check inputs at the controller or service layer.
  2. Use schemas: Define strict data models (e.g., JSON Schema, OpenAPI).
  3. Sanitize inputs: Even after validation, use filter_var() or similar to escape unsafe characters before database insertion.
  4. Use HTTP Status Codes:
    • 400: Bad Request (malformed JSON).
    • 422: Unprocessable Entity (validation errors).
  5. Centralize validation: Move rules and messages to a separate class or helper class for reusability.
  6. Log errors: Log validation failures for debugging or security monitoring.
  7. Document your API: Use OpenAPI/Swagger to specify expected inputs and error responses.
  8. Handle edge cases: Test with empty strings, null values, malformed JSON, and oversized payloads.