From Service to Swagger: Leveraging Ballerina’s Automatic OpenAPI Generation
Ballerina’s automatic OpenAPI generation turns your HTTP service into a ready‑to‑use Swagger spec. Learn how to leverage annotations, serve the spec, and validate it in a few simple steps.
28 Apr 2026, 22:37 UTC

Why the Manual Swagger Loop is a Pain
When you expose a REST endpoint, you usually write the code first and then hand‑craft a Swagger/OpenAPI spec to document it. That spec must mirror every path, query, header, body schema, and response code. A typo in the spec can break auto‑generated clients, while a change in the service forces a new spec update. The cycle is error‑prone and wastes developer hours.
Thesis: Ballerina Generates a Full OpenAPI 3.0 Document for You
Starting with Ballerina 2201, the compiler automatically emits a compliant OpenAPI 3.0 JSON file when you build an HTTP service. No extra tooling, no manual YAML/JSON, no extra maintenance. The generated spec lives under the same project, can be served at /openapi.json, and is immediately consumable by Swagger‑UI, Postman, or any OpenAPI validator.
How Ballerina Infers the Spec
Ballerina maps the language constructs of an HTTP service to OpenAPI elements:
- Service path →
pathsentries - Request parameters (path, query, header, cookie) →
parameters - Request body type →
requestBodyschema - Response types and status codes →
responses - Custom types (records, objects) →
components.schemas
The compiler walks the service definition, collects type information, and serializes it into JSON. If a service uses only HTTP, the spec is automatically created; other protocols like gRPC are ignored.
Fine‑Grained Control with Custom Annotations
While the default inference is accurate for most cases, you can tweak the output with annotations that live next to the service or the function:
@OpenAPIDescription("summary", "description")– sets the operation summary and description.@OpenAPIResponse(200, "Successful response", MyResponseType)– explicitly declares a response code, description, and schema.@OpenAPIConsumes("application/json")– forces theconsumesfield for a particular operation.
These annotations are optional; if omitted, the compiler uses the inferred defaults. They are especially useful when you need to expose a non‑standard status code or provide richer documentation for a client.
Serving the Generated Spec
Once the project is built, Ballerina automatically registers a /openapi.json endpoint. To see it:
- Compile the service:
bal build myservice - Run the executable:
bal run target/bin/myservice - Open http://localhost:8080/openapi.json in a browser or a tool like Swagger‑UI.
The JSON you receive is a fully valid OpenAPI 3.0 document. You can copy it into any OpenAPI‑compatible workflow, or point Swagger‑UI’s url to the endpoint for live documentation.
Worked Example
Below is a minimal service that demonstrates the feature. It exposes a /greet endpoint that accepts a path parameter name and an optional query parameter lang. The response is a simple record. The service is annotated to add a description and a custom 404 response.
import ballerina/http;
import ballerina/log;
@http:ServiceConfig {basePath: "/"}
service /greetService {
@OpenAPIDescription(
"Get a greeting message",
"Returns a greeting in the requested language."
)
@OpenAPIResponse(
404,
"Name not found",
null
)
@http:ResourceConfig {path: "/greet/{name}", methods: ["GET"]}
resource function greet(http:Caller caller, http:Request req, string name) {
string? lang = req.getQueryParams().get("lang");
if (lang == "fr") {
return createResponse(caller, name, "Bonjour");
} else if (lang == "es") {
return createResponse(caller, name, "Hola");
} else {
return createResponse(caller, name, "Hello");
}
}
function createResponse(http:Caller caller, string name, string greeting) returns error? {
var resp = new http:Response();
resp.setPayload({greeting: greeting + ", " + name + "!"});
return caller->respond(resp);
}
}
record Greeting = {
string greeting;
};
After building and running, navigate to http://localhost:8080/openapi.json. You should see an openapi field set to 3.0.0, a paths entry for /greet/{name}, parameters for name and lang, and a responses block containing 200 and 404 entries. The components.schemas section will include the Greeting record.
Trade‑offs and Limitations
- Scope: Only HTTP services trigger spec generation. Services exposing gRPC or other protocols do not have an OpenAPI output.
- Complex Types: Polymorphic or recursive types may be simplified. Verify the generated schema with a validator if you rely on advanced type features.
- Annotation Accuracy: Missing or malformed annotations can lead to incomplete or invalid specs. Always run an OpenAPI validator after building.
- Version Compatibility: The feature is available from Ballerina 2201 onward. Older projects need manual spec generation or third‑party tools.
Actionable Checklist
- Upgrade to Ballerina 2201+ if you haven’t already.
- Define your HTTP services as usual; add
@OpenAPIDescriptionand@OpenAPIResponsewhere needed. - Build with
bal buildand run withbal run. - Validate the spec:
curl http://localhost:8080/openapi.json | openapi‑validator - Serve the JSON to Swagger‑UI:
swagger-ui --spec http://localhost:8080/openapi.json - For CI/CD, commit the generated JSON or automate its extraction from the build output.
By following these steps, you eliminate a manual, error‑prone step from your API development workflow and gain a single source of truth that keeps documentation and code in sync.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.