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
CamelHttpMethodexplicitly 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
onExceptionsyntax is tricky - avoid complex error handling initially- Use
suppressExceptions: truewith 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:
- Call primary backend → store response in property
- Extract key field (e.g., customerId) using jsonpath
- Call secondary backend using extracted key
- 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-tableselector (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/plainfor 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.29Health Checks
- Camel JBang startup can be slow (60-90s)
- Set generous
start_periodfor 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