Hello World Service in Ballerina: Complete Solution & Deep Dive Guide

A ballerina poses gracefully in a dance.

Learn Ballerina from Zero: Building Your First HTTP Service

Creating a "Hello, World!" HTTP service in Ballerina involves defining an http:Listener on a specific port and creating a service that attaches to it. The service then exposes a resource function that responds to GET requests on a defined path, returning the classic string as the response payload.

You've spent hours, maybe even days, wrestling with complex frameworks. Setting up a simple API endpoint feels like assembling a thousand-piece puzzle in the dark. You're bogged down by boilerplate code, dependency injection containers, and configuration files before you can even write a single line of business logic. The dream of rapid, cloud-native development feels distant. This is a common struggle for developers navigating the modern microservices landscape.

But what if there was a language designed from the ground up to solve this exact problem? A language where network services aren't an afterthought bolted on with a library, but a core, first-class citizen. This is the promise of Ballerina. In this guide, we'll strip away all the unnecessary complexity and build a fully functional "Hello, World!" web service from scratch. You'll discover how Ballerina's intuitive syntax and powerful abstractions make network programming feel natural and efficient, getting you from idea to running endpoint in minutes, not hours.


What Exactly is a Ballerina HTTP Service?

Before we dive into the code, it's crucial to understand the fundamental building blocks of a web service in Ballerina. Unlike general-purpose languages where you import a web framework (like Express.js for Node or Spring Boot for Java), Ballerina has these concepts built directly into its syntax. This makes the code not only more concise but also incredibly clear about its intent.

The core components are:

  • Listener: A Listener is an object that waits for incoming network requests on a specific port. For our web service, we'll use an http:Listener. It's the entry point for all traffic to our application. Think of it as the receptionist at the front door of your building, directing visitors to the right office.
  • Service: A service is a collection of resources that are exposed to the network. It attaches itself to a Listener to handle the incoming requests. If the Listener is the receptionist, the Service is the specific office or department the visitor wants to reach. A single application can have multiple services attached to one or more listeners.
  • Resource Function: Inside a service, you define resource functions. These are the actual handlers for specific requests. They map directly to HTTP methods (like GET, POST, PUT) and URL paths. This is the specific person in the office who handles the visitor's request. The combination of the HTTP method and the path determines which resource function gets executed.

This "Listener -> Service -> Resource" model is a powerful and explicit way to define network interactions, making Ballerina code exceptionally readable and maintainable, especially as applications grow in complexity.

  ● Client Request (e.g., from a browser or curl)
  │
  ▼
┌──────────────────────────┐
│ Network (Internet/LAN)   │
└───────────┬──────────────┘
            │
            ▼
┌──────────────────────────┐
│ Ballerina http:Listener  │
│ (Listening on Port 9090) │
└───────────┬──────────────┘
            │
            ▼
┌──────────────────────────┐
│ Ballerina Service        │
│ (Attached to Listener)   │
└───────────┬──────────────┘
            │
            ▼
┌──────────────────────────┐
│ Resource Function        │
│ (Matches Path & Method)  │
└───────────┬──────────────┘
            │
            ▼
┌──────────────────────────┐
│ Your Business Logic      │
│ (Returns "Hello, World!")│
└───────────┬──────────────┘
            │
            ▼
  ● Client Receives Response

Why Use Ballerina for Web Services?

In a world saturated with programming languages and frameworks, a new entry needs a compelling reason to exist. Ballerina's "raison d'être" is to simplify cloud-native development and system integration. It's not trying to be another Java or Python; it's purpose-built for the connected world of APIs and microservices.

The Core Advantages

  • Simplicity and Readability: The service-oriented syntax means your code looks like the system you're building. There's minimal boilerplate, allowing you to focus purely on logic.
  • Network-Aware Type System: Ballerina understands network data types like JSON and XML natively. This eliminates the tedious manual parsing and serialization common in other languages. You define a data structure, and Ballerina handles the conversion to and from JSON automatically.
  • Built-in Concurrency: Ballerina is designed for concurrent operations from the ground up. Its model of "workers" and "strands" makes it easy to handle thousands of simultaneous requests efficiently without getting lost in the complexities of manual thread management.
  • Graphical by Design: A unique feature of Ballerina is that its code has a dual representation. You can write the code and automatically generate a sequence diagram that visually represents the logic and network interactions. This is invaluable for documentation and understanding complex flows.
  • Explicit Error Handling: Ballerina forces developers to think about what can go wrong. Its robust error handling mechanism with the check keyword ensures that potential failures are handled gracefully, leading to more resilient applications.

Pros and Cons at a Glance

No technology is a silver bullet. It's important to understand where Ballerina shines and what its potential drawbacks are.

Pros (Strengths) Cons (Weaknesses / Considerations)
First-class network primitives: service, listener, and client objects are part of the language itself. ⚠️ Smaller Ecosystem: Compared to giants like Java or Python, the library and community support ecosystem is still growing.
Reduced Boilerplate: Significantly less code is needed to create a functional API compared to traditional frameworks. ⚠️ Niche Focus: It is highly optimized for integration and network services. It may not be the best choice for CPU-intensive tasks like machine learning or for desktop application development.
Automatic JSON Serialization/Deserialization: Seamlessly converts between Ballerina records and JSON payloads. ⚠️ Learning Curve: While the syntax is clean, developers must learn new concepts like strands, workers, and the specific service-oriented paradigm.
Visual Representation: Code can be automatically visualized as sequence diagrams, aiding comprehension and documentation. ⚠️ Maturity: While stable and production-ready, it hasn't been "battle-tested" for as many decades as languages like Java or C++.

How to Build the "Hello World" HTTP Service

Now, let's get our hands dirty. We will walk through the entire process, from setting up the project to writing the code, running the service, and testing it. This simple exercise is the first step in the Ballerina Learning Roadmap and forms the foundation for all future network programming tasks.

Step 1: Project Setup

First, ensure you have Ballerina installed. Open your terminal and create a new Ballerina project. The Ballerina toolchain makes this incredibly simple.


# Create a new project named 'hello_service'
bal new hello_service

# Navigate into the newly created project directory
cd hello_service

This command scaffolds a new project with a default main.bal file. We will modify this file to create our HTTP service.

Step 2: The Solution Code

Open the main.bal file inside your project and replace its contents with the following code. This is the complete solution for our "Hello, World!" service, based on the exclusive learning materials from kodikra.com.


import ballerina/http;
import ballerina/log;

// A service is a network-accessible API.
// The `service / on new http:Listener(9090)` declaration creates a new
// service that attaches to a listener on port 9090.
// The `/` defines the base path for all resources within this service.
service / on new http:Listener(9090) {

    // A resource function is an operation that can be invoked by a client.
    // The `get` keyword specifies that this function responds to HTTP GET requests.
    // `greeting` becomes the path for this specific resource.
    // The full path will be the service's base path + the resource's path: /greeting
    // This function is defined to return a `string` or potentially an `error`.
    resource function get greeting() returns string|error {
        
        // Log a message to the console for observability.
        // This is good practice for debugging.
        log:printInfo("GET request received for /greeting");

        // The core logic: return the classic string.
        // Ballerina automatically sets the HTTP status to 200 OK and the
        // Content-Type header to text/plain.
        return "Hello, World!";
    }

}

Step 3: Detailed Code Walkthrough

Let's break down that code piece by piece to understand what's happening under the hood.

    ● Start: main.bal
    │
    ├─ import ballerina/http; ───> Brings in HTTP listener, service, and client capabilities.
    │
    ├─ import ballerina/log;  ───> Provides functions for logging output to the console.
    │
    ▼
  ┌───────────────────────────┐
  │ service / on new ...      │ ◀─ Defines a service.
  └───────────┬───────────────┘
              │
              ├─ "/" ──────────────> Sets the base path for all endpoints in this service.
              │
              └─ new http:Listener(9090) ─> Creates a listener object that waits for
                 │                         requests on TCP port 9090.
                 ▼
               ┌──────────────────────────┐
               │ resource function get ...│ ◀─ Defines an endpoint within the service.
               └──────────┬───────────────┘
                          │
                          ├─ "get" ──────────> Maps this function to the HTTP GET method.
                          │
                          ├─ "greeting" ─────> Sets the sub-path. Full URL: /greeting.
                          │
                          └─ returns string|error ─> Declares the function will return either
                             │                       a string (success) or an error (failure).
                             ▼
                           ┌──────────────────────────┐
                           │ return "Hello, World!"; │ ◀─ The function's logic.
                           └──────────┬───────────────┘
                                      │
                                      ▼
                                  ● End: A string payload is sent back to the client.
  • import ballerina/http;
    This line imports Ballerina's standard HTTP module. This module provides all the necessary types and functions for building HTTP clients and services, including the http:Listener and the service declaration syntax.
  • import ballerina/log;
    This imports the logging module, which is a standard practice for creating observable applications. The log:printInfo() function allows us to see in the terminal when our endpoint is being hit.
  • service / on new http:Listener(9090) { ... }
    This is the heart of the application.
    • service ... { }: This keyword declares a new service.
    • /: This is the base path of the service. All resource paths inside this service will be relative to this. In this case, it's the root path.
    • on new http:Listener(9090): This is the magic. It declaratively creates a new http:Listener object that listens on port 9090 and immediately attaches this service to it. This one line replaces dozens of lines of setup code in other frameworks.
  • resource function get greeting() returns string|error { ... }
    This defines the actual API endpoint.
    • resource function: This special function type is used inside services to define network-accessible resources.
    • get: This is the accessor. It directly maps to the HTTP GET method. Other accessors include post, put, delete, etc.
    • greeting: This is the path segment for this resource. The full path is calculated by combining the service's base path (/) with the resource's path (greeting), resulting in /greeting.
    • returns string|error: This is the function's signature. It explicitly states that this function will either return a string on success or an error if something goes wrong. This strong typing and explicit error handling are key features of Ballerina.
  • return "Hello, World!";
    This is the implementation. When a GET request is made to /greeting, this function executes and returns the string. Ballerina's HTTP module is smart enough to know that a returned string should be sent as the response body with an HTTP 200 OK status code and a Content-Type: text/plain header.

Step 4: Running and Testing the Service

With the code in place, running it is a single command.


# From within your 'hello_service' project directory, run the service:
bal run

You will see output indicating that the service has started and is listening for requests.


Compiling source
        your_org/hello_service:0.1.0

Running executable

[ballerina/http] started HTTP/WS listener 0.0.0.0:9090

Your service is now live! To test it, open a new terminal window (leaving the service running in the first one) and use a tool like curl to send an HTTP GET request.


# Send a GET request to your running service
curl http://localhost:9090/greeting

You should immediately see the response:


Hello, World!

If you look back at the terminal where your Ballerina service is running, you'll also see the log message we added:


time = 2023-10-27T10:30:00.123Z level = INFO module = your_org/hello_service message = "GET request received for /greeting"

Congratulations! You have successfully built, run, and tested your first web service in Ballerina.


Alternative Approach: Returning JSON

In modern applications, it's far more common to return structured data like JSON rather than plain text. Ballerina's network-aware type system makes this incredibly easy. Let's modify our service to return a JSON object.

First, we define a record type to represent the structure of our JSON.


import ballerina/http;
import ballerina/log;

// Define a record type to structure our response.
// This will be automatically converted to a JSON object.
type Greeting record {|
    string message;
    string author = "kodikra.com"; // Default value
|};

service / on new http:Listener(9090) {

    // The resource function now returns our custom `Greeting` type.
    resource function get greeting() returns Greeting|error {
        log:printInfo("GET request received for /greeting (JSON)");

        // Create an instance of our record and return it.
        return { message: "Hello, World!" };
    }

}

Now, when you run this service and test it with curl, you'll get a different result:


# Use the -v flag to see the response headers
curl -v http://localhost:9090/greeting

# ... (headers will show Content-Type: application/json) ...
{"message":"Hello, World!", "author":"kodikra.com"}

Notice what happened. We simply changed the return type from string to our custom Greeting record. Ballerina's HTTP module detected this, automatically serialized our record into a JSON string, and set the Content-Type header to application/json for us. This powerful, built-in feature saves a tremendous amount of boilerplate code and is a cornerstone of Ballerina's design philosophy.

For more advanced topics and to continue your journey, we highly recommend exploring the complete Ballerina guide available on our platform.


Frequently Asked Questions (FAQ)

What is the difference between a `service` and a `class` in Ballerina?

A class in Ballerina is a template for creating objects with state (fields) and behavior (methods), similar to classes in other object-oriented languages. A service, however, is a language construct specifically designed to represent a network-accessible API. It's built for exposing logic over a network, attaching to listeners, and containing resource functions. While a service is technically an object, its syntax and purpose are specialized for integration and communication.

How can I change the port number my service runs on?

You can change the port number directly in the listener's initialization. Simply modify the number passed to the http:Listener constructor. For example, to run the service on port 8080, you would change the line to: service / on new http:Listener(8080). It's also common practice to read the port from a configuration file for better flexibility in different environments.

How would I handle a POST request instead of a GET?

You would define another resource function using the post accessor. The function signature would also include a parameter to capture the incoming request body. For example: resource function post greeting(json payload) returns json { ... }. Ballerina would automatically deserialize the incoming JSON request body into the payload variable.

What does the `/` in `service / on ...` actually mean?

The / represents the base path for all the resource functions defined within that service block. If you changed it to service /api/v1 on ..., then your greeting resource function would become accessible at the URL http://localhost:9090/api/v1/greeting. This provides a clean way to version and group your APIs.

Why use a `resource function` instead of a regular `function`?

A resource function is a special type of function that can only be defined inside a service. Its syntax, with accessors (get, post) and path segments, is designed to map directly to the principles of RESTful APIs. A regular function is a general-purpose block of code that can be called from anywhere and doesn't have this direct mapping to network protocols.

Is Ballerina a compiled or interpreted language?

Ballerina is a compiled language. When you execute bal run or bal build, the Ballerina source code (.bal files) is compiled into Java bytecode (.jar files) that runs on the Java Virtual Machine (JVM). This gives it the performance benefits of a compiled language and the portability of the JVM ecosystem.

Where can I learn more about Ballerina's capabilities?

This "Hello, World!" service is just the beginning. The kodikra Ballerina Learning Roadmap provides a structured path to mastering concepts like client connectors, data manipulation, concurrency, and deployment. We encourage you to continue with the next module to build on this foundation.


Conclusion: Your Journey into Cloud-Native Development

In just a few lines of code, you've built and run a robust, production-ready HTTP service. You didn't need to choose a framework, configure a server, or write complex serialization logic. You simply described the service you wanted, and Ballerina's language-native features handled the rest. This is the core philosophy of Ballerina: to provide developers with the right tools and abstractions to build the connected systems of today and tomorrow with clarity and confidence.

The "Hello, World!" service is more than just a beginner's exercise; it's a demonstration of a paradigm shift in how we approach network programming. By elevating concepts like listeners and services to be first-class citizens, Ballerina empowers you to write code that is a direct reflection of your system's architecture. As you progress, you'll find this clarity invaluable when building complex microservice ecosystems, API gateways, and data integration pipelines.

Disclaimer: All code examples and concepts are based on Ballerina Swan Lake 2201.8.x and newer versions. The syntax and features may differ in older versions of the language. Always refer to the official documentation for the version you are using.


Published by Kodikra — Your trusted Ballerina learning resource.