Use Case

API Contract Testing

Validate your API responses match expectations

API Contract Testing

API contracts define what clients expect from your APIs. When responses deviate from the contract — missing fields, wrong types, changed formats — client applications break. APIAssert helps you continuously verify your APIs honor their contracts.

The Problem

API contract violations cause real problems:

  • Mobile apps crash — Required field becomes optional
  • Frontend breaks — String becomes number, parsing fails
  • Integrations fail — Partner's code expects specific format
  • Silent data loss — Renamed fields aren't mapped correctly

These issues often slip past unit tests and only surface in production.

How APIAssert Helps

Validate Response Structure

Assert that expected fields exist:

Monitor: User API Contract
URL: GET /api/users/123
Assertions:
  ✓ $.id exists
  ✓ $.email exists
  ✓ $.created_at exists
  ✓ $.profile exists
  ✓ $.profile.name exists

Verify Data Types

Ensure fields have correct types:

Assertions:
  ✓ $.id is string
  ✓ $.age is number
  ✓ $.active is boolean
  ✓ $.tags is array
  ✓ $.metadata is object

Check Value Constraints

Validate values meet expectations:

Assertions:
  ✓ $.status in ["active", "pending", "disabled"]
  ✓ $.email matches email pattern
  ✓ $.price > 0
  ✓ $.items.length > 0
  ✓ $.page <= $.total_pages

Contract Testing Assertions

Required Fields

// Expected contract:
{
  "id": "string (required)",
  "name": "string (required)",
  "email": "string (required)",
  "avatar": "string (optional)"
}

Assertions:

  • $.id must exist
  • $.name must exist
  • $.email must exist
  • $.avatar may or may not exist (no assertion)

Field Types

// Expected types:
{
  "count": 42,
  "name": "Product",
  "active": true,
  "tags": ["sale", "new"],
  "metadata": {"key": "value"}
}

Assertions:

  • $.count is number
  • $.name is string
  • $.active is boolean
  • $.tags is array
  • $.metadata is object

Enum Values

// Expected enum:
{
  "status": "active" | "pending" | "cancelled"
}

Assertions:

  • $.status in ["active", "pending", "cancelled"]

Nested Objects

// Expected structure:
{
  "user": {
    "profile": {
      "address": {
        "city": "string"
      }
    }
  }
}

Assertions:

  • $.user.profile.address.city must exist
  • $.user.profile.address.city is string

Arrays

// Expected array structure:
{
  "items": [
    {"id": "string", "name": "string"}
  ]
}

Assertions:

  • $.items is array
  • $.items.length >= 0
  • $.items[0].id exists (if non-empty)
  • $.items[0].name exists (if non-empty)

Real-World Example

The Scenario

A mobile app consumes a user profile API. A backend refactor changes user.name to user.full_name. The API returns 200 OK, but the app crashes because it expects name.

The APIAssert Solution

Monitor: User Profile Contract

URL: GET /api/v1/users/me
Headers:
  Authorization: Bearer $TEST_TOKEN
Assertions:
  ✓ $.user.id exists
  ✓ $.user.name exists          ← Catches the rename!
  ✓ $.user.name is string
  ✓ $.user.email exists
  ✓ $.user.email matches "@"
  ✓ $.user.created_at exists
  ✓ $.user.avatar is string or null
Interval: 5 minutes

The Outcome

  • Contract violation detected in staging before production deploy
  • Alert fired when $.user.name assertion failed
  • Breaking change caught before mobile app update
  • Fix applied — kept name field alongside full_name

Common API Contracts to Monitor

User/Account APIs

GET /api/users/:id
Required fields:
  - $.id (string)
  - $.email (string, email format)
  - $.created_at (string, ISO date)
  - $.status (enum: active, disabled)

Optional fields:
  - $.profile.name (string)
  - $.profile.avatar (string, URL)

Collection/List APIs

GET /api/products
Required fields:
  - $.data (array)
  - $.pagination.page (number)
  - $.pagination.total (number)
  - $.pagination.per_page (number)

Array item contract:
  - $.data[*].id (string)
  - $.data[*].name (string)
  - $.data[*].price (number, > 0)

Error Responses

Any endpoint returning 4xx/5xx
Required fields:
  - $.error.code (string)
  - $.error.message (string)

Optional fields:
  - $.error.details (array)
  - $.error.request_id (string)

Webhook Payloads

POST webhook
Required fields:
  - $.event (string, enum)
  - $.timestamp (string, ISO date)
  - $.data (object)
  - $.data.id (string)

Best Practices

Version Your Contracts

Monitor each API version separately:

Monitor: User API v1 Contract
URL: /api/v1/users/123

Monitor: User API v2 Contract
URL: /api/v2/users/123

Test Production Data

Use real (or realistic) test accounts:

# Good: Production test user
GET /api/users/test_user_12345

# Less good: Minimal test
GET /api/users/1

Monitor Breaking vs Non-Breaking

Breaking changes (critical):

  • Required field removed
  • Type changed
  • Enum value removed

Non-breaking changes (warning):

  • Optional field removed
  • New field added
  • Enum value added

Create Contract Per Consumer

Different clients may have different needs:

Monitor: Mobile App Contract
- Strict field requirements
- Specific enum values

Monitor: Partner Integration Contract
- Different field subset
- Version-specific

Document Your Contracts

Keep assertions aligned with documentation:

OpenAPI/Swagger → Generate assertions
Assertions → Validate in production

Continuous Contract Testing

Development Workflow

1. Define contract in OpenAPI
2. Create APIAssert monitors from contract
3. Monitor staging environment
4. Deploy to production
5. Monitor production continuously

When to Alert

Severity Condition Action
Critical Required field missing Page on-call
High Type changed Slack alert
Medium New unexpected field Email
Low Optional field removed Log only

Alert Response

When contract violation detected:

  1. Identify the change — What field/type changed?
  2. Check recent deploys — Who changed what?
  3. Assess impact — Which clients affected?
  4. Decide action — Rollback or fix forward?
  5. Update contracts — If intentional change

Getting Started

  1. Document your contracts — What fields are required?
  2. Create monitors — One per major endpoint
  3. Add structure assertions — Required fields exist
  4. Add type assertions — Fields have expected types
  5. Add value assertions — Enums, ranges, patterns
  6. Set alerts — Critical for breaking changes

Related Features