The Problem: API Documentation Drift is the Default

API documentation has a persistent reliability problem. The code evolves, but the OpenAPI specification often lags behind, creating a disconnect. Conversely, if the spec is updated without corresponding code changes or client regeneration, the client-side code breaks. This documentation drift is the default state for many APIs. The causes are well-understood: documentation and code are maintained separately, updating docs requires extra discipline with every pull request, and no automated checks catch route signature changes not reflected in the OpenAPI file. The consequence is significant: clients building against a stale specification encounter integration errors in production. This is a fundamental challenge in API development, leading to wasted developer time and unreliable integrations.

Baklava's Solution: Generate from Truth

Baklava, an open-source library from Iterators, tackles documentation drift by generating documentation directly from the tests that verify the actual API behavior. This approach ensures that the documentation can never drift from the live system because it’s derived from the system’s own validation mechanisms. By treating tests as the single source of truth for API contracts, Baklava guarantees that the documentation accurately reflects the current state of the API. This structural solution eliminates the need for manual updates to separate documentation files, which are prone to human error and oversight.

The library focuses on Scala routing tests, a common way to define and verify API endpoints in Scala applications. Instead of writing an OpenAPI specification and then separately ensuring the Scala routes match it, Baklava flips the workflow. Developers write their routing tests as usual. Baklava then inspects these tests to infer the API structure, generating an OpenAPI specification. From this specification, it can then generate type-safe clients for various languages. This means that if a routing test passes, the generated documentation and clients are guaranteed to be consistent with that test.

How Baklava Works: From Scala Tests to OpenAPI

Baklava integrates with popular Scala routing libraries like http4s and Tapir. The core idea is to parse the routing definitions within the tests. These definitions specify the HTTP method, path, request parameters, request body, response status codes, and response bodies. Baklava analyzes these elements to construct an OpenAPI v3 specification.

For instance, a typical http4s route might be defined like this:

import org.http4s._
import org.http4s.dsl.io._

def helloRoutes[IO[_]: Effect]: HttpRoutes[IO] = HttpRoutes.of {
  case GET -> Root / "hello" / name =>
    Ok(s"Hello, $name!")
  case POST -> Root / "users" =>
    // ... handle user creation ...
    Created("User created")
}

Baklava would inspect this route. It recognizes the HTTP method (GET), the path segments (`/hello/` followed by a dynamic segment `name`), and the response (a plain text string). From this, it can infer an OpenAPI path item with a GET operation, specifying a path parameter named `name` and a string response for a 200 OK status.

Similarly, for a POST request to `/users` that returns a 201 Created status, Baklava can infer the operation and its expected response. The library aims to automatically detect common patterns and data structures used in request and response bodies, often leveraging Scala’s type system and libraries like Circe for JSON handling. The goal is to minimize manual annotation within the tests, allowing developers to focus on writing functional tests.

Diagram illustrating Baklava's workflow from Scala tests to OpenAPI and client generation.

Generating Type-Safe Clients

Once Baklava has generated an OpenAPI specification from the Scala routing tests, it can then use this specification to generate type-safe clients. This process typically involves leveraging existing OpenAPI client generation tools, such as OpenAPI Generator or various language-specific libraries. The generated clients ensure that when a developer uses them in their application, the calls they make to the API will match the expected structure defined in the OpenAPI spec, which in turn matches the tests.

For example, if your Scala API has a route that accepts a JSON payload for creating a user, and this is defined and tested, Baklava generates an OpenAPI spec. This spec can then be used by OpenAPI Generator to produce a TypeScript client. This client would have a `createUser` function that expects a specific TypeScript interface for the user data and returns a promise that resolves with the expected response structure. Any attempt to call `createUser` with incorrect data types or structure would be caught at compile time by the TypeScript compiler, not at runtime during integration.

This tight coupling between the API implementation, its documentation, and its client libraries significantly reduces integration friction. It means that developers consuming the API can be confident that the client code they are using is aligned with the server’s actual behavior, as verified by the server’s own tests.

Implications and Future Considerations

Baklava’s approach addresses a critical pain point in API development. By situating documentation generation within the testing phase, it promotes a more robust and maintainable API ecosystem. This is particularly valuable for organizations with complex Scala backends and multiple client applications, where synchronization issues can lead to significant development overhead.

The library's success hinges on its ability to accurately infer API contract details from a wide variety of testing patterns and routing library configurations. As Baklava matures, we can expect it to support more Scala routing frameworks and potentially offer more granular control over the OpenAPI generation process. The surprising detail here is not the existence of such a tool, but its focus on leveraging existing tests as the primary artifact for documentation and client generation, a workflow that feels more natural for developers.

What nobody has addressed yet is how Baklava handles complex error scenarios or edge cases that might be tested but not explicitly documented in a typical OpenAPI schema. Will it infer custom error response bodies and status codes, or will these require specific annotations within the tests? This is a key area for future development and community contribution.

For developers working with Scala APIs, adopting Baklava means shifting the mindset around API documentation. Instead of an afterthought, documentation and client generation become an intrinsic part of the testing process. This requires a disciplined approach to writing comprehensive routing tests that cover all aspects of the API contract.