Skip to main content
Request validation helps you catch integration issues early by ensuring requests match expected schemas. Apicentric validates request bodies, query parameters, headers, and path parameters.

Why validate requests

  • Catch errors early: Identify invalid requests before they reach your application
  • Document expectations: Schemas serve as live documentation
  • Contract testing: Ensure clients send properly formatted data
  • Debugging: Get clear error messages when requests don’t match

Basic request body validation

Define the expected structure for request bodies:
endpoints:
  - method: POST
    path: /users
    request_body:
      content_type: application/json
      schema: |
        {
          "username": "string",
          "email": "string",
          "age": "number"
        }
    responses:
      201:
        content_type: application/json
        body: |
          {
            "id": {{faker "datatype.number"}},
            "username": "{{request.body.username}}",
            "email": "{{request.body.email}}",
            "age": {{request.body.age}}
          }
      400:
        content_type: application/json
        body: |
          {
            "error": "Validation failed",
            "message": "Request body does not match expected schema"
          }
1
Test valid request
2
curl -X POST http://localhost:9000/api/users \
  -H "Content-Type: application/json" \
  -d '{
    "username": "alice",
    "email": "[email protected]",
    "age": 25
  }'
3
Test invalid request
4
curl -X POST http://localhost:9000/api/users \
  -H "Content-Type: application/json" \
  -d '{
    "username": "alice",
    "age": "not-a-number"
  }'
5
You’ll receive a 400 error with validation details.

Validating query parameters

Define expected query parameters:
endpoints:
  - method: GET
    path: /products
    description: List products with filtering
    parameters:
      - name: category
        in: query
        required: false
        type: string
        description: Filter by product category
      - name: limit
        in: query
        required: false
        type: integer
        description: Maximum number of results
      - name: min_price
        in: query
        required: false
        type: number
        description: Minimum price filter
    responses:
      200:
        content_type: application/json
        body: |
          {
            "category": "{{query.category}}",
            "limit": "{{query.limit}}",
            "min_price": "{{query.min_price}}",
            "products": []
          }
Test it:
curl "http://localhost:9000/api/products?category=electronics&limit=10&min_price=99.99"

Required vs optional fields

Control which fields are mandatory:
endpoints:
  - method: POST
    path: /orders
    request_body:
      content_type: application/json
      schema: |
        {
          "customer_id": "number",
          "items": [{"product_id": "number", "quantity": "number"}],
          "notes": "string?"
        }
    responses:
      201:
        content_type: application/json
        body: |
          {
            "order_id": {{faker "datatype.number" min=1000 max=9999}},
            "customer_id": {{request.body.customer_id}},
            "items": {{json request.body.items}},
            "status": "pending"
          }
      422:
        condition: "{{not request.body.customer_id}}"
        content_type: application/json
        body: |
          {
            "error": "Validation failed",
            "details": ["customer_id is required"]
          }
Use the ? suffix to mark fields as optional: "field": "string?". All fields without ? are required by default.

Validating headers

Ensure required headers are present:
endpoints:
  - method: GET
    path: /profile
    description: Get user profile (requires authentication)
    header_match:
      Authorization: "Bearer *"
      X-API-Version: "v1"
    responses:
      200:
        content_type: application/json
        body: |
          {
            "id": 1,
            "name": "User",
            "email": "[email protected]"
          }
      401:
        content_type: application/json
        body: |
          {
            "error": "Unauthorized",
            "message": "Missing or invalid Authorization header"
          }
Test with headers:
curl http://localhost:9000/api/profile \
  -H "Authorization: Bearer abc123" \
  -H "X-API-Version: v1"

Complex nested schemas

Validate deeply nested objects:
endpoints:
  - method: POST
    path: /checkout
    request_body:
      content_type: application/json
      schema: |
        {
          "customer": {
            "id": "number",
            "email": "string"
          },
          "items": [
            {
              "product_id": "number",
              "quantity": "number",
              "price": "number"
            }
          ],
          "shipping": {
            "address": "string",
            "city": "string",
            "zip": "string",
            "country": "string"
          },
          "payment": {
            "method": "string",
            "card_last4": "string?"
          }
        }
    responses:
      201:
        content_type: application/json
        body: |
          {
            "checkout_id": {{faker "datatype.uuid"}},
            "total": {{faker "commerce.price"}},
            "status": "pending"
          }

Validation error responses

Provide helpful error messages when validation fails:
endpoints:
  - method: POST
    path: /api/register
    request_body:
      content_type: application/json
      schema: |
        {
          "email": "string",
          "password": "string",
          "age": "number"
        }
    responses:
      201:
        condition: "{{and request.body.email request.body.password}}"
        content_type: application/json
        body: |
          {
            "id": {{faker "datatype.number"}},
            "email": "{{request.body.email}}"
          }
      400:
        condition: "{{not request.body.email}}"
        content_type: application/json
        body: |
          {
            "error": "Validation failed",
            "field": "email",
            "message": "Email is required"
          }
      422:
        condition: "{{lt request.body.age 18}}"
        content_type: application/json
        body: |
          {
            "error": "Validation failed",
            "field": "age",
            "message": "Must be 18 or older"
          }

Validating content types

Ensure the correct Content-Type header:
endpoints:
  - method: POST
    path: /upload
    request_body:
      content_type: multipart/form-data
    responses:
      200:
        content_type: application/json
        body: |
          {
            "uploaded": true,
            "filename": "{{request.body.filename}}"
          }
      415:
        content_type: application/json
        body: |
          {
            "error": "Unsupported Media Type",
            "message": "Expected multipart/form-data"
          }

Using validation helpers

Apicentric provides template helpers for common validations:
responses:
  400:
    condition: "{{not (contains request.body.email '@')}}"
    content_type: application/json
    body: |
      {
        "error": "Invalid email format"
      }

Testing validation

Create a comprehensive test suite:
# Valid request
curl -X POST http://localhost:9000/api/users \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "email": "[email protected]", "age": 25}'

# Missing required field
curl -X POST http://localhost:9000/api/users \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "age": 25}'

# Wrong type
curl -X POST http://localhost:9000/api/users \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "email": "[email protected]", "age": "not-a-number"}'

# Invalid email format
curl -X POST http://localhost:9000/api/users \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "email": "invalid-email", "age": 25}'

Debugging validation failures

Enable verbose logging to see validation details:
apicentric simulator start --services-dir . --verbose
You’ll see detailed logs for each request:
📥 Request: POST /api/users
📋 Headers: {"Content-Type": "application/json"}
📦 Body: {"username": "alice", "age": 25}
❌ Validation failed: Missing required field 'email'
📤 Response: 400 Bad Request
Validation errors return 400 Bad Request by default. Customize error responses using the condition field to return different status codes like 422 Unprocessable Entity.

Best practices

  1. Start simple: Begin with basic type validation, then add complexity
  2. Clear error messages: Help developers understand what went wrong
  3. Document schemas: Use the description field to explain requirements
  4. Test edge cases: Validate boundary conditions and invalid inputs
  5. Use realistic data: Test with data that matches production scenarios

Next steps

Contract testing

Validate your mocks against real APIs

Import specs

Import existing OpenAPI specs with validation

Export specs

Generate OpenAPI specs from your services

Creating mocks

Learn more about mock API features

Build docs developers (and LLMs) love