Keeping Ballerina REST APIs and OpenAPI Specs in Sync
Learn how Ballerina’s @Service and @Resource annotations let the compiler generate OpenAPI 3.0 docs that stay in sync with your REST implementation.
08 Jul 2025, 02:20 UTC

The problem: drifting API docs
When you maintain a REST service, the OpenAPI document often falls out of sync with the actual implementation. Manual edits are easy to forget, leading to mismatched request/response schemas, broken client generation, and confusing documentation.
Takeaway: By defining Ballerina services with @Service and @Resource annotations, the compiler can derive a correct OpenAPI 3.0 description directly from the code, keeping the spec and implementation in lockstep.
How Ballerina maps annotations to OpenAPI
Each @Resource function becomes an operation. The annotation’s path and method fields populate the OpenAPI path and operation. Parameter annotations (@QueryParam, @Header, @PathParam) become query, header, or path parameters. The function’s return type and any @BodyParam are used to generate request and response schemas from Ballerina records, maps, or JSON/XML types. The compiler collects all of this into an OpenAPI document.
Worked example: a greeting service
import ballerina/http;
import ballerina/log;
@http:ServiceConfig { basePath: '/greet' }
service greet on new http:Listener(9090) {
@http:ResourceConfig {
path: '/{name}',
method: 'GET'
}
function greet(string name) returns string|error {
log:printInfo('Greeting ' + name);
return 'Hello, ' + name;
}
}Running the OpenAPI generator:
ballerina openapi greet.balThis creates greet.openapi.yaml in the same directory. The file contains a /greet/{name} path with a GET operation, a path parameter name of type string, and a 200 response whose schema is a string. (The exact YAML is omitted here to avoid inventing output.)
You can verify the generated document matches the running service:
- Start the service:
bal run greet.bal - In another terminal, fetch the runtime OpenAPI:
curl -s http://localhost:9090/openapi.json - Compare the two files, for example with
diff -u greet.openapi.yaml <(curl -s http://localhost:9090/openapi.json | yq -P)(you needyqto convert JSON to YAML). If there is no output, the documents are identical.
Limitations and when to add manual annotations
- The generator captures only standard HTTP concepts defined by Ballerina’s mapping rules. Custom headers, non‑standard status codes, or vendor extensions require the
openapi:addannotation. - Security schemes (OAuth2, API key, etc.) are not inferred; you must declare them explicitly with
openapi:SecuritySchemeand reference them viaopenapi:Securityon the service or resource. - If you need to override a generated description or summary, use
openapi:Descriptionoropenapi:Summaryon the resource function.
These manual annotations are additive; they do not interfere with the auto‑generated parts.
Actionable checklist
- Write your service using
@Serviceand@Resource(or@http:ServiceConfigand@http:ResourceConfigas shown). - Run
ballerina openapi filename.balto produce the OpenAPI file. - Start the service and curl
<host>:<port>/openapi.jsonto confirm runtime consistency. - If you need custom headers, security, or non‑standard responses, add the appropriate
openapi:annotations. - Keep the generated file under version control; treat it as a derived artifact and regenerate after any change to the service.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.