Greeting Service in Ballerina: Complete Solution & Deep Dive Guide

A ballerina poses gracefully in a dance.

The Complete Guide to Building Your First Ballerina HTTP Service

Creating your first Ballerina HTTP service involves defining an http:Service that attaches to an http:Listener. You then implement a resource function that accepts a payload, like a string, processes it by concatenating "Hello, ", and returns the result, which Ballerina automatically sends as the HTTP response.

Have you ever stared at a new programming language, especially one built for the complexities of the modern web, and wondered where to begin? The traditional "Hello, World!" printed to a console feels disconnected from what languages like Ballerina are designed for: network communication. You know you need to build APIs and services, but the leap from a simple print statement to a functioning web endpoint can feel like a chasm.

This guide bridges that gap. We're not just printing text; we're building a live, interactive Greeting Service. You will learn the fundamental building blocks of any Ballerina web application: listeners, services, and resource functions. By the end, you won't just have code; you'll have a deep understanding of how Ballerina handles network requests with elegant simplicity, setting you on a clear path to building powerful microservices.


What Is a Ballerina HTTP Service?

In the world of modern software, a service is a self-contained piece of functionality that can be accessed over a network. A Ballerina HTTP Service is a specialized, first-class construct in the language designed specifically to handle HTTP/HTTPS requests. Unlike general-purpose languages where you might import heavy frameworks to build a web server, Ballerina has these concepts built into its very syntax and standard library.

The core components you'll interact with are:

  • http:Listener: This is the entry point for network traffic. It listens on a specific network port (like 9090 or 80) for incoming HTTP requests. Think of it as the front door to your application.
  • http:Service: This is the logical container for your API's functionality. You attach a service to a listener, telling Ballerina, "Any requests that come through that door should be handled by this service." A service defines a base path, like /api/v1, for all its resources.
  • resource function: This is the heart of your service. Each resource function maps directly to an API endpoint—a combination of an HTTP method (GET, POST, PUT, etc.) and a URL path segment. This is where your actual business logic lives.

Ballerina's design philosophy is to make network programming explicit and clear. Instead of abstract classes and complex configurations, you declare a listener, declare a service on that listener, and write resource functions. This declarative approach makes the code exceptionally readable and mirrors the very structure of the API you're building.


Why Choose Ballerina for Web Services?

While you can build web services in almost any language, Ballerina was engineered from the ground up to solve the challenges of network integration and microservice development. Its design choices offer distinct advantages that streamline the development process and enhance reliability.

The Power of Network-Aware Typing

One of Ballerina's standout features is its type system, which understands network data formats. When you define a resource function like resource function post greeting(string payload), Ballerina performs a process called payload data binding. It automatically inspects the incoming request's Content-Type header and attempts to convert the request body into the type you specified (in this case, a string). This eliminates mountains of boilerplate code for parsing and validation that you would typically write yourself.

This concept extends seamlessly to complex types. You can define a Ballerina record that mirrors a JSON object, and Ballerina will automatically deserialize the incoming JSON into your typed record, providing compile-time safety for your API logic.

Simplified Concurrency with Strands

Every incoming request to a Ballerina service is handled on a lightweight, non-blocking thread called a strand. This is managed entirely by the Ballerina runtime. You don't need to worry about manual thread management, async/await complexities, or callback hell. This built-in concurrency model ensures that your service can handle a high volume of requests efficiently without compromising code readability.

A Visual and Declarative Syntax

Ballerina's syntax is designed to be self-documenting. The way you define services and resources visually represents the API's structure. This clarity is so integral that the Ballerina toolchain can automatically generate sequence diagrams from your code, providing a clear visual representation of the logic and network interactions.

Pros and Cons of Using Ballerina for Services

Pros Cons / Risks
Simplified Concurrency: Automatic handling of requests on lightweight strands makes scaling easy. Smaller Ecosystem: The community and third-party library ecosystem is still growing compared to giants like Java or Node.js.
Strongly Typed Payloads: Automatic data binding for JSON, XML, and basic types reduces runtime errors and boilerplate. Learning Curve: While clear, the paradigm is different from traditional OOP or functional languages, which can require some adjustment.
Integration-Focused: Built-in connectors and tools for interacting with various protocols and services. Evolving Toolchain: As a modern language, the tooling and features are constantly evolving, which can require keeping up with updates.
Built-in Security Features: The standard library includes robust support for authentication, authorization, and transport-level security. Niche Talent Pool: Finding experienced Ballerina developers can be more challenging than for mainstream languages.

How to Build, Run, and Test the Greeting Service

Now, let's dive into the practical steps. We'll start by setting up the project, writing the code, and finally, testing our live endpoint. This process is the fundamental workflow for any Ballerina application.

Step 1: Project Setup

First, open your terminal. The Ballerina toolchain provides commands to create and manage projects. We'll create a new project called greeting_service.

bal new greeting_service

This command scaffolds a new Ballerina project with a standard directory structure and a default main.bal file. Navigate into the newly created directory:

cd greeting_service

You can now open this folder in your favorite code editor, like VS Code, which has an excellent Ballerina extension that provides syntax highlighting, code completion, and graphical previews.

Step 2: The Complete Solution Code

Replace the contents of main.bal with the following code. This is our complete Greeting Service, with comments explaining each part.


import ballerina/http;

// 1. The Listener: The entry point for network traffic.
// This creates an HTTP listener named 'httpListener' that will accept
// incoming requests on port 9090. If another process is using this port,
// the service will fail to start.
listener http:Listener httpListener = new (9090);

// 2. The Service: The logical container for our API.
// We declare a 'service' that handles requests on the root path ('/').
// This service is 'attached' to our 'httpListener', meaning any request
// received by the listener will be routed to this service if the path matches.
service / on httpListener {

    // 3. The Resource Function: The API endpoint logic.
    // This function handles POST requests to the '/greeting' endpoint.
    // For example, a request to 'http://localhost:9090/greeting'.
    //
    // - 'resource function': A special function type within a service.
    // - 'post': The HTTP method this function responds to.
    // - 'greeting': The path segment for this specific resource.
    // - '(string payload)': This is payload data binding. Ballerina will
    //   automatically try to convert the body of the POST request into a string.
    // - 'returns string': Specifies that this function will return a string,
    //   which Ballerina will automatically use as the HTTP response body
    //   with a 'Content-Type' of 'text/plain'.
    resource function post greeting(string payload) returns string {
        
        // 4. The Business Logic: Simple string concatenation.
        // We create a new string by prepending "Hello, " to the received payload.
        string message = "Hello, " + payload;

        // The returned value is sent back to the client as the response.
        return message;
    }
}

Step 3: Visualizing the Request Flow

To understand what's happening under the hood, let's visualize the journey of a single request through our Ballerina service.

  ● Client (e.g., cURL, browser)
  │
  │ Sends POST /greeting with "World"
  ▼
┌───────────────────────────┐
│   Network Interface       │
└────────────┬──────────────┘
             │ Port 9090
             ▼
      ┌────────────────┐
      │ http:Listener  │
      └───────┬────────┘
              │ Request received
              ▼
         ┌──────────────────┐
         │ service /        │ Routes based on path
         └────────┬─────────┘
                  │ Path matches '/greeting'
                  ▼
       ◆ Is method POST?
      ╱           ╲
    Yes           No (405 Method Not Allowed)
     │
     ▼
┌───────────────────────────────┐
│ resource function post greeting │
│   (string payload = "World")  │
└───────────────┬───────────────┘
                │ Executes logic
                ▼
      ┌─────────────────────────┐
      │ return "Hello, World"   │
      └─────────┬───────────────┘
                │
                │ Ballerina wraps this in an HTTP 200 OK Response
                ▼
  ● Client receives the response

Step 4: Detailed Code Walkthrough

Let's break down the code into its constituent parts to ensure every line is crystal clear.

  • import ballerina/http;
    This line imports the official Ballerina HTTP module, which contains all the necessary types and functions for building web services, such as http:Listener, http:Service, and the underlying client/server logic.
  • listener http:Listener httpListener = new (9090);
    Here, we instantiate the listener. http:Listener is the type, httpListener is our variable name, and new(9090) is the constructor call that creates the listener object and binds it to TCP port 9090 on the local machine.
  • service / on httpListener { ... }
    This is the service declaration. The / specifies that this service handles requests starting from the root path of the server. The on httpListener part is crucial; it attaches this service logic to our previously defined listener. All resource functions inside this block will be relative to this base path.
  • resource function post greeting(string payload) returns string
    This is the most information-dense line.
    • resource function: Declares a network-accessible function.
    • post: Binds this function to the HTTP POST method.
    • greeting: Binds this function to the path segment /greeting. The full path is the service path + resource path, which is / + greeting = /greeting.
    • (string payload): This is the input parameter. Ballerina's data binding magic will extract the body of the POST request and convert it into a string variable named payload. If the body is not compatible, Ballerina will automatically respond with a 400 Bad Request error.
    • returns string: This declares the function's return type. When a resource function returns a basic type like string, Ballerina automatically creates an HTTP 200 OK response, sets the Content-Type header to text/plain, and uses the returned value as the response body.
  • return "Hello, " + payload;
    This is the core logic of our application. It performs simple string concatenation and returns the result, which, as explained above, becomes the HTTP response sent back to the client.

Step 5: Running and Testing the Service

With the code saved in main.bal, it's time to bring our service to life. In your terminal, from within the greeting_service directory, run the following command:

bal run

You should see output indicating that the service has started, typically something like:

Compiling source
        user/greeting_service:1.0.0

Running executable

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

Your service is now running and listening for requests on port 9090. To test it, open a *new* terminal window (leaving the service running in the first one) and use a command-line tool like cURL to send a POST request.

curl -X POST -H "Content-Type: text/plain" -d "Ballerina Developer" http://localhost:9090/greeting

Let's break down this cURL command:

  • -X POST: Specifies that we are sending an HTTP POST request.
  • -H "Content-Type: text/plain": Sets the content type header, telling the server that the data we're sending (the body) is plain text. This helps Ballerina's data binding mechanism.
  • -d "Ballerina Developer": Provides the data for the request body. This string is what will be bound to our payload parameter.
  • http://localhost:9090/greeting: The URL of our endpoint.

If everything is working correctly, you will receive the following response in your terminal:

Hello, Ballerina Developer

Congratulations! You have successfully built, run, and tested a real Ballerina HTTP service.


Where This Pattern Fits: Use Cases and Alternatives

The simple service we've built is a foundational pattern. It's the "atom" of microservice architecture. This exact structure—a listener, a service, and a resource function that accepts a payload—is the basis for countless real-world applications.

Common Use Cases

  • Simple REST APIs: This is the most direct application. You can expand this service with more resource functions (GET, PUT, DELETE) to create a full CRUD (Create, Read, Update, Delete) API.
  • Webhook Handlers: Services like GitHub, Stripe, or Slack send notifications (webhooks) to a URL you provide. This pattern is perfect for creating an endpoint to receive and process those webhook payloads.
  • Data Transformation Services: A service can act as a lightweight intermediary that receives data in one format (e.g., plain text), transforms it (e.g., enriches it, converts it to JSON), and passes it to another system.
  • Façade Services: It can act as a simple public-facing API that hides the complexity of calling multiple downstream services in the backend.

When to Consider Alternatives: Handling Complex Data

Our service handles a simple string. But what about more structured data, like JSON? This is where Ballerina's type system truly shines. Let's say you want to receive a JSON object with a user's name and age.

First, you would define a record to represent that structure:


// Define a record to match the expected JSON structure
type User record {
    string name;
    int age;
};

Then, you would simply change the type of the payload parameter in your resource function:


// The resource function now expects a JSON payload matching the User record
resource function post user(User payload) returns string {
    return string `Hello, ${payload.name}! You are ${payload.age} years old.`;
}

That's it! Ballerina's data binding will now expect a request with Content-Type: application/json and a body like {"name": "Alice", "age": 30}. It will automatically parse the JSON and validate it against your User record. If the JSON is malformed or missing fields, Ballerina sends a 400 Bad Request response for you.

Visualizing Advanced Data Binding

This automated process is a core strength of the language.

  ● HTTP POST Request
  │  ├─ Header: Content-Type: application/json
  │  └─ Body: {"name": "Alice", "age": 30}
  ▼
┌───────────────────────────┐
│   Ballerina HTTP Module   │
└────────────┬──────────────┘
             │
             ▼
      ◆ Inspects Headers & Signature
      │  ├─ Sees 'Content-Type: application/json'
      │  └─ Sees 'resource function post user(User payload)'
      ▼
┌───────────────────────────┐
│ Automatic Deserialization │
│   JSON Text → Ballerina   │
│           'User' record   │
└────────────┬──────────────┘
             │
             │ If successful, creates a 'User' variable
             ▼
┌──────────────────────────────────┐
│   Calls resource function with   │
│   populated 'payload' variable   │
│   (payload.name = "Alice")       │
│   (payload.age = 30)             │
└──────────────────────────────────┘

Frequently Asked Questions (FAQ)

1. What is the difference between a `service` and a `resource function` in Ballerina?
A service is a collection of related endpoints that share a common base path and are attached to a listener. A resource function is the implementation of a single endpoint within that service, defined by its HTTP method (e.g., post) and path segment (e.g., greeting).
2. Can I use the `GET` method instead of `POST` to send a name?
Yes, but the mechanism is different. GET requests typically don't have a body. You would use query parameters instead. The function signature would change to use the @http:Query annotation: resource function get greeting(@http:Query string name) returns string { ... }. You would then call it with a URL like http://localhost:9090/greeting?name=World.
3. How do I handle errors, like if the payload is not in the expected format?
For data binding, Ballerina handles this automatically by returning a 400 Bad Request status. For custom logic errors, you can return an error or a specific HTTP status code response, like return http:badRequest("Invalid input provided");. The function's return type would need to be updated to include the error, for example: returns string|error.
4. What does "payload data binding" actually do behind the scenes?
It's a content-aware process. The Ballerina HTTP module inspects the Content-Type header of the incoming request. Based on the header and the type of the parameter in your resource function, it selects a marshaller to convert the raw byte stream of the request body into the specified Ballerina type (e.g., string, json, xml, or a custom record).
5. How can I change the port from 9090 to something else?
You can change the port number directly in the listener's instantiation. To use the standard HTTPS port, you would change the line to: listener http:Listener httpListener = new (443, config = {secureSocket: {...}});, which also requires providing security configurations like a certificate and key file.
6. Is Ballerina suitable for large-scale, production applications?
Absolutely. Ballerina was designed specifically for building robust, scalable, and maintainable network applications and microservices. Its strong typing, built-in concurrency model, and focus on integration make it an excellent choice for enterprise-grade systems.
7. Where can I learn more about Ballerina's HTTP capabilities?
The official Ballerina documentation is the best resource. You can also explore our comprehensive Ballerina guides for more in-depth tutorials and examples covering topics from RESTful APIs to GraphQL services and gRPC clients.

Conclusion: Your First Step into Network-Native Development

You have successfully navigated the fundamentals of creating a web service in Ballerina. More than just writing code, you've learned the philosophy behind it: the clear, declarative structure of listeners and services, the power of automatic payload data binding, and the simplicity of its built-in concurrency. This Greeting Service, while simple, is not a toy—it's a fully-fledged microservice that embodies the core principles of modern API development.

This foundation is your launchpad. From here, you can explore handling complex JSON, interacting with databases, securing your endpoints, and connecting to other APIs. The pattern remains the same, allowing you to build complex systems with the same clarity and confidence you've established today.

We encourage you to experiment. Change the return message, add another resource function for a different HTTP method, or try handling a JSON payload as described in the alternatives section. The best way to learn is by building. To continue on this structured learning journey, explore the next module in the kodikra Ballerina learning path.

Disclaimer: The code and concepts discussed in this article are based on Ballerina Swan Lake 2201.x.x (the current stable release). Syntax and module APIs may evolve in future versions of the Ballerina language.


Published by Kodikra — Your trusted Ballerina learning resource.