Files
agiledancers/adr/approved/ADR2_http-api-contracts-and-tooling.md
Wanja Krah bd898e92d2
All checks were successful
Build, test and publish / verify (push) Successful in 8m15s
S19 refinement
2026-11-08 13:39:16 +01:00

10 KiB

ID: ADR2
Title: HTTP API contracts and tooling
Supersedes: -
Status: APPROVED
Expiration Date: Six months after approval; record the calendar date upon manual approval.

Context

Agile Dancers currently uses REST without an explicit architectural decision. The landscape needs agreed defaults for communication across system boundaries, contract documentation, client generation, compatibility, and error handling.

Interactions include frontend access, requests between business systems, delegation of long-running calculations to workers, change notifications, and external consumers. Most clients and servers are developed by the same maintainer. Consumers must nevertheless be able to upgrade independently. The independently deployable systems proposed in ADR1 are relevant context; ADR1 remains REFINED and is not approved through this decision.

The current frontend uses Angular, but future clients may use other frameworks or languages. Contracts and tooling should support that flexibility.

For now, interactions use a defined alternative path when another system is offline. Durable queued delivery is not a current requirement. More specialized communication mechanisms may become appropriate for particular interactions.

Decisions

Scope and communication style

Use HTTP APIs with JSON payloads as the default across system boundaries. Use resource-oriented endpoints where natural and action-oriented endpoints where they express business intent more clearly. Do not force business actions into CRUD operations. For example:

GET  /api/v1/schedules/{id}
POST /api/v1/schedules/{id}/calculate

Action endpoints still follow HTTP method and status semantics. For long-running work, use an explicit job or operation resource with a status endpoint when the caller needs to track progress. A notification can initially use a direct HTTP request with documented failure behavior; this does not promise durable delivery.

These are landscape defaults. Document justified exceptions alongside the relevant interface, including the reason and the alternative contract and failure behavior. This ADR does not mandate GraphQL, RPC, or messaging infrastructure.

Contracts and documentation

Describe HTTP APIs using OpenAPI. Normally generate the description from server code, adding explicit descriptions and annotations where needed. Review the resulting contract as a public interface; generation alone does not ensure completeness or backward compatibility.

Use self-hosted Scalar API Reference as the default interactive documentation UI. Keep the OpenAPI document independently available so consumers and tooling do not depend on the UI. Publish a contract artifact for each supported major version.

Choose an OpenAPI specification version supported by the server tooling, Scalar, and the selected client generators. Do not automatically adopt the latest version without checking that compatibility.

Client generation

Generate client transport code and contract types from OpenAPI by default, using OpenAPI Generator. Select the generator appropriate to the consuming technology; use typescript-angular for Angular clients. This choice does not require future frontends to use Angular.

Pin generator versions and configuration for reproducible generation. Generate against a selected contract artifact rather than requiring a running server during the client build. Keep generated code separate from handwritten application logic and do not manually edit generated files. Generated static types do not by themselves provide runtime response validation.

Versioning and compatibility

Put the major contract version in the URL, such as /api/v1/schedules. Keep backward-compatible changes within that major version. Introduce a new major version for breaking changes. Software release and generated client package versions may advance independently of the major contract version.

Allow consumers to upgrade independently. Normally support one contract version; during transitions, aim to support two. This is a guideline rather than a hard cap: additional versions may coexist when justified. Document supported versions, deprecation, and the transition plan, coordinating retirement with affected consumers. Check compatibility before releasing contract changes.

Errors and offline behavior

Standardize API error bodies on RFC 9457 Problem Details using application/problem+json and appropriate HTTP status codes. Use stable problem type identifiers for programmatic handling and readable explanations for humans. Clients must not depend on parsing human-readable messages. Document expected errors in OpenAPI, including validation details where applicable. Do not expose internal exception details or sensitive information in error responses.

Define timeouts and the offline alternative path for each interaction. A network failure may produce no HTTP response, so consumers must handle that separately from a Problem Details response. Avoid automatic retries of actions unless repeating them is safe. Do not assume durable delivery or eventual replay.

Adoption and review

Adopt incrementally for new interfaces and interfaces being substantially changed. Migrate existing interfaces through planned work while preserving compatibility for existing consumers. No immediate landscape-wide rewrite is required.

Review six months after approval, or earlier if integration needs expose limitations. The review date triggers reevaluation rather than automatic expiry.

Options considered

Communication approach

Option Pros Cons Outcome
Resource-oriented HTTP/JSON with action endpoints Readable resources and business actions; supports different client technologies; retains HTTP semantics. Requires consistent endpoint conventions and explicit handling of long-running work and failures. Chosen as the default.
GraphQL Flexible data selection; typed schema; supports different consumer views of related data. Adds query authorization and performance concerns; requires additional conventions for work delegation and notifications. Not chosen: current needs and common client/server ownership do not justify the added complexity.
RPC, including JSON-RPC and gRPC Explicit operation contracts; JSON-RPC is lightweight; gRPC supports strongly typed generated clients. Operation-oriented contracts require careful evolution; gRPC adds browser integration requirements. Not chosen as the default: resource-oriented HTTP with optional actions better matches the desired interface flexibility. RPC remains a possible justified exception.

Contract description and authoring

Option Pros Cons Outcome
OpenAPI, normally generated from server code Language-independent HTTP contracts; supports documentation and client generation; avoids separately maintaining a design language. Needs annotations, review, and compatibility checks; generated output can be incomplete. Chosen.
TypeSpec generating OpenAPI Dedicated contract authoring language; can generate descriptions and other artifacts. Adds a language and compiler to maintain. Not chosen: no current benefit sufficient to justify the additional tooling.
RAML API description language with reusable modeling constructs. Its specification repository is archived, creating a maintenance concern for a new standard. Not chosen.

Interactive documentation

Option Pros Cons Outcome
Scalar API Reference Readable interactive documentation; integrated request testing; self-hosting and configuration options. Requires integration and maintenance alongside the server framework. Chosen: readability and testability are the primary purposes of the UI.
Swagger UI Interactive requests and authentication support; direct integration with Quarkus OpenAPI tooling. Its expandable operation layout is less aligned with the preferred reading experience. Not chosen as the landscape default.

Client generation

Option Pros Cons Outcome
OpenAPI Generator Supports Angular and other client technologies in one tool family; configurable generation. Generator-specific options and upgrades need management. Chosen to support future technology changes.
ng-openapi-gen Angular-focused generation; npm-based tooling. Other client technologies require another generator. Not chosen as the landscape default.
Handwritten clients Direct control over client code and application integration. Duplicates contract types and request details; increases drift risk. Not chosen as the default.

Consequences

  • Apply the defaults to new and substantially changed interfaces. Plan migration of existing interfaces incrementally, preserving consumer compatibility.
  • Generate and publish versioned OpenAPI artifacts. Add Scalar to provide interactive documentation and request testing for supported contracts.
  • Establish pinned OpenAPI Generator configurations for client technologies, initially Angular. Keep generated code separate from application logic.
  • Add contract validation and compatibility checks to release workflows. Verify generated clients against the actual contract before adopting generator upgrades.
  • Introduce URL major versions and coordinate deprecation with consumers. Normally maintain one version and temporarily two, documenting justified deviations.
  • Align API error responses with Problem Details and document expected failures. Define timeout and offline behavior per interaction, including safe retry rules.
  • Record justified exceptions alongside their interfaces rather than imposing an unsuitable communication mechanism on every interaction.
  • Record the calendar review date upon manual approval. REFINED captures the agreed proposal; approval remains a separate manual step.

References