Domain APIs: Lessons Learned

Category: Domain APIs Last Updated: 2026-01-28

Lessons learned from domain API projects including gateway patterns, backend orchestration, and API design.

Camel YAML DSL (JBang)

Query Parameter Handling

  • Query parameters arrive as HTTP headers in Camel REST DSL
  • Store them in exchange properties early: ${header.acknowledgementReference}${exchangeProperty.ackRef}
  • Null checks: Use ${exchangeProperty.ackRef} != null (simple expression)
  • Complex conditions with && can be unreliable - prefer nested choices or store in properties first

Sequential HTTP Calls

  • Critical: Set CamelHttpMethod explicitly between calls:
    - setHeader:
        name: CamelHttpMethod
        constant: "GET"
  • Remove Camel HTTP headers between calls: removeHeaders: pattern: "CamelHttp*"
  • Without explicit method, subsequent calls may use wrong HTTP verb (405 errors)

Groovy Transformation

  • Requires two dependencies:
    --dep=org.apache.camel:camel-groovy
    --dep=org.apache.groovy:groovy-json:4.0.29
  • groovy-json is separate from camel-groovy and provides JsonSlurper/JsonOutput
  • Store intermediate responses in properties before transformation:
    - setProperty:
        name: taxPlatformResponse
        simple: "${body}"

YAML DSL Gotchas

  • onException syntax is tricky - avoid complex error handling initially
  • Use suppressExceptions: true with jsonpath to avoid failures on missing fields
  • Route IDs must be unique across all route files

Orchestration Patterns

Sequential Backend Calls

Pattern for enriching responses from multiple backends:

  1. Call primary backend → store response in property
  2. Extract key field (e.g., customerId) using jsonpath
  3. Call secondary backend using extracted key
  4. Combine responses using Groovy/transformation

Example flow:

tax-platform (get submission)
  → extract customerId
  → customer service (get trader)
  → merge into enriched response

Response Combination

Using Groovy to merge JSON responses:

def submission = slurper.parseText(exchange.getProperty('primaryResponse', String))
def customer = slurper.parseText(exchange.getProperty('secondaryResponse', String))
submission.trader = [name: customer.name, type: customer.type]
return JsonOutput.toJson(submission)

Testing Strategies

Acceptance Test Structure

  • domain-api tests: Direct HTTP calls to API endpoints (vitest)
  • UI tests: Swagger UI interaction tests (Playwright)
  • Integration tests: Backend mock validation (jest)

Prism Mock Behavior

  • Prism returns example data for ANY valid request format
  • Non-existent resources still return 200 with example data
  • Accept multiple status codes in tests: expect([200, 422]).toContain(response.status)
  • 422 can occur randomly due to Prism’s validation behavior

Swagger UI Testing (Playwright)

  • With tryItOutEnabled: true, Execute button is visible immediately
  • Query parameters may need explicit filling even with examples defined
  • Use input[placeholder="paramName"] to find parameter inputs
  • Verify execution with .live-responses-table selector (not response code cells)
  • Response code cells appear in both schema definitions AND live responses - be specific

Test Resilience

  • Accept ranges of status codes for mock-backed tests
  • Content-type may be text/plain for error responses
  • Check headers only on successful responses

Docker/JBang Setup

Dependencies

Add Camel dependencies via command line:

command: >
  run /routes/integration.yaml
  --dep=org.apache.camel:camel-jacksonxml
  --dep=org.apache.camel:camel-groovy
  --dep=org.apache.groovy:groovy-json:4.0.29

Health Checks

  • Camel JBang startup can be slow (60-90s)
  • Set generous start_period for health checks:
    healthcheck:
      start_period: 90s

Container Recreation

  • Changing command/dependencies requires container recreation
  • Use docker-compose up -d <service> to recreate with new config

Error Handling

Simplified Approach

  • Start without complex onException blocks
  • Return clear JSON error responses with code/message
  • Handle missing required params at route entry point
  • Let backend errors propagate for debugging initially

Status Code Mapping

  • 400: Missing/invalid query parameters
  • 404: Resource not found (may be 200 from Prism mocks)
  • 422: Validation errors
  • 503: Backend unavailable (for fault injection testing)

Performance

Parallel vs Sequential

  • Sequential calls are simpler to implement and debug
  • Consider parallel calls only when backends are independent
  • Camel multicast can parallelize, but adds complexity

Extracted from VPD Domain API POC work (Phase 7a/7b). See also: Workspace Lessons