Build a JSON REST Endpoint in Ballerina Using the http Module
A practical guide to creating a Ballerina service that returns JSON over HTTP, controlling status codes with explicit http return types, and verifying the result with curl.
26 Sept 2026, 17:58 UTC

You need a Ballerina service that answers HTTP GET requests with JSON, but a resource function that returns a bare error produces an opaque HTTP 500 with no control over the response body. The useful takeaway is to declare the resource with a union return type that includes explicit http status records such as http:Ok or http:NotFound, then verify the listener with curl before relying on it.
Desired Outcome: A JSON Endpoint on Port 9090
The goal is a Ballerina service exposing an HTTP resource that returns JSON, runnable with bal run and callable via curl. The service should bind to a known port, map a clean URL path to a resource function, and return a content type of application/json.
Prerequisites: Toolchain and Package Scaffold
- Ballerina CLI. Run
bal versionin your terminal. Expected: a version string. If the command is not found, the CLI is not on your PATH. - Package scaffold. Run
bal new json_endpointin a user-writable directory. Expected: a new directory containingBallerina.tomland a.balsource file. - Standard library module. The
ballerina/httpmodule ships with the distribution but is resolved like any dependency. Offline or air-gapped builds need cached or local modules.
Procedure: Listener, Resource Function, and Return Types
Open the generated .bal file and replace its contents with the following service definition:
import ballerina/http;
service /api on new http:Listener(9090) {
resource function get greeting() returns json|error {
return {"message": "hello"};
}
}
Key elements:
service /apisets the base path to/api.new http:Listener(9090)binds the service to TCP port 9090.resource function get greeting()maps HTTP GET requests for the path segmentgreetingto this function, making the full route/api/greeting.returns json|erroris a union type: success returns a JSON value, failure returns an error.- If the resource calls an external HTTP client, use the
checkexpression to propagate errors instead of swallowing them.
Controlling Status Codes with http Return Types
Returning a bare error from the resource makes Ballerina generate a generic 500 response. To control the status code and body, widen the return type to include http status records. For example, change the signature to returns json|http:Ok|http:NotFound and return the appropriate value. As a diagnostic decision, temporarily return an http:NotFound value; a subsequent curl call should show a 404 status, confirming that path routing and the listener are active.
Expected Checks: Build, Run, and curl
- Compile and resolve dependencies. Run
bal buildin the package root. Expected: the build succeeds and dependencies resolve. Risk: unresolved imports if the http module is not cached. - Start the service. Run
bal runin the same terminal. Expected: the listener starts and the process remains in the foreground. Risk: port conflict if 9090 is already bound. - Call the endpoint. From a second terminal, run
curl -i http://localhost:9090/api/greeting. Expected: a 200 status line,Content-Type: application/json, and a body containing the JSON object. Risk: connection refused if the service is not running.
Recovery Options for Common Failures
- Port conflict. Change
9090in the listener declaration to an unused port, then re-run. - Unresolved imports. Run
bal pullor verify thatBallerina.tomllists the dependency; for offline use, confirm the module exists in the local distribution cache. - Type errors. Replace inferred types with explicit
jsonor record types when the compiler rejects a return value.
Limitations and Version Notes
Ballerina Swan Lake (2201.x) changed service syntax; older 1.x listener-variable style is not interchangeable. Confirm your installed version with bal version before adapting code from older tutorials. Exact current version numbers and release dates change; consult the official Ballerina distribution for the latest stable release. The http module is standard library but still resolved as a dependency; air-gapped environments require pre-cached modules.
Practical Verification
The curl -i output is the primary check. If you see the expected status line, content type, and payload, the endpoint is working. If the status or body is wrong, use the recovery options above to isolate listener, routing, or type issues.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.