From Service to Spec: How Ballerina’s Built‑In OpenAPI Generation Accelerates API‑First Development
Ballerina’s built‑in <code>openapi</code> command turns annotated services into a full OpenAPI 3.0 spec and optional client stubs, keeping contracts in sync with the code and speeding up API‑first workflows.
13 Sept 2025, 01:41 UTC

Problem: You Need an API Contract, but You’re Writing Code First
When you write a Ballerina service, you start with annotated functions that expose HTTP endpoints. The code is clear, but external teams, documentation generators, and client libraries all want a formal contract – an OpenAPI 3.0 spec. Traditionally, you would hand‑craft that spec or use a separate tool that parses the running service at runtime. This adds friction and risks the spec falling out of sync with the code.
Thesis: The ballerina openapi Command Automates Contract Generation
Ballerina ships a lightweight CLI command that walks your service source, extracts HTTP verbs, path parameters, and payload schemas, and emits a compliant OpenAPI 3.0 JSON file. From the same source you can also generate client stubs in Java, JavaScript, or Go. The result is a single, version‑controlled contract that lives in the same repository as the service code.
1. Service Code – Annotate, Build, and Run
Below is a minimal example. Save it as service.bal in a project folder.
import ballerina/http;
import ballerina/io;
type Greeting record { string message; };
service /greet on new http:Listener(9090) {
@http:GET
@http:Path("/hello/{name}")
function sayHello(http:Caller caller, http:Request req, string name) {
Greeting greet = { message: "Hello, " + name + "!" };
var payload = greet;
var response = new http:Response();
response.setPayload(payload);
_ = caller->respond(response);
}
}
Build the service to ensure there are no compile errors:
# Run from the folder containing service.bal
ballerina build service.bal
2. Generate the OpenAPI 3.0 Spec
After a successful build, run:
ballerina openapi service.bal
# The command creates service-openapi.json in the same folder.
The generated JSON will contain the /greet/hello/{name} path, the GET method, and a schema for the Greeting record. Open the file and look for a paths section that mirrors the service code.
3. Create Client Stubs for External Projects
From the same spec you can generate a client in the language of your choice. For example, to get a Java client:
ballerina openapi -o client --lang java service.bal
# The client source is written to ./client/java.
The generated Java classes compile against the spec and provide type‑safe methods that match the service’s endpoints.
4. Keep the Spec in Sync with Your Code
Whenever you add, remove, or change an endpoint, simply re‑run the ballerina openapi command. The spec will reflect the current state of the source. Because the command is separate from the runtime build, it does not affect deployment performance.
5. Trade‑offs & Limitations
- Custom Ballerina annotations or generic types may not be fully represented; manual edits to the JSON may be required.
- Dynamic routing or runtime‑only logic (e.g., path resolution based on configuration) is not expressible in a static spec and will be omitted.
- The feature requires Ballerina 1.5 or newer; older releases lack the
openapicommand. - Large services with many endpoints can produce sizeable spec files; consider splitting services if the spec grows beyond a few megabytes.
6. Actionable Checklist
- Annotate your Ballerina service with
@http:GET,@http:POST, etc. - Build the service:
ballerina build service.bal. - Generate the spec:
ballerina openapi service.bal→service-openapi.json. - If needed, generate a client stub:
ballerina openapi -o client --lang java service.bal. - Commit the spec (and client if generated) to version control alongside the source.
- Re‑run the
openapicommand after each code change that affects the contract.
By treating the OpenAPI file as first‑class source, you eliminate the “contract‑vs‑code” drift that plagues many API‑first projects. The Ballerina CLI gives you a fast, repeatable path from service to spec to client, all within the same repository.
Diagram
| Service Code | OpenAPI Spec | Client Stub | Runtime |
|---|---|---|---|
| service.bal | service-openapi.json | Java/JS/Go client | HTTP Listener |
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.