Ballerina
I’ve been planning to take a look at Ballerina for a year or so now. Ballerina bills itself as a “Cloud Native Programming Language”. It is designed to make it easy to write microservices with integrated APIs. Ballerina is an open source, compiled, statically and strongly typed, language. If you’ve written any Java and, to a lesser extent, Go, it’s going to feel familiar. It comes with a concurrent execution model and is built with a set of pluggable imports for systems integration, for example plugging in third-party APIs like Twitter or adding a database backend. One of the more interesting aspects of the language is native types for JSON, XML and Table-based data, designed to make it easier to handle the formats that are the lingua franca of the API ecosystem.
This post walks through some basic Ballerina. It’s not opinionated on whether Ballerina is a good choice for you or not: you should do your own requirements analysis for that. :)
Hello World (API)
Ballerina focusses on quickly building services. Let’s take a quick look at a (functional) snippet of Ballerina that is the Hello World (API edition) of the language:
|
|
First, we’ve imported Ballerina’s HTTP package. This contains all the pieces needed to build an HTTP endpoint for a service.
Next, we’ve created a HTTP listener endpoint on port 9090. It has a service, how Ballerina describes a network-addressable API, this one exposed on the /hello
path, bound to that listener. Inside that service, we have a resource, an invokable API method, called sayHello
, available on the path /hello/sayHello
. The method has a caller
, the client invoking the resource and a request
object representing a HTTP request.
Inside the resource, we define an object, response
, to hold our HTTP response and return that to the caller
. We then call a function, setTextPayload
, on the response
object to set its payload as Yo James!\n
. Finally, we pass that response to the caller:
|
|
Here _
ignores any errors and our response
is delivered via a synchronous network-bound call, indicated by the ->
.
Pretty neat in all of 13 lines of code.
Let’s install Ballerina and run the example.
Installing Ballerina
You can get Ballerina on the Downloads page. There are installer packages for Windows, Mac OS, and Linux. I’m running Linux, so I am just going to download and install the Deb package.
|
|
The package provides a ballerina
binary.
|
|
Ballerina is currently pre-1.0, with the associated “here be dragons”, but the last few releases have been locking down the language and other features.
Running Hello World (API)
Now let’s copy our Hello World code into a file:
|
|
Ballerina code has the file extension, .bal
, and generally files are named for the service they contain. Paste our code into this file and we can run it with Ballerina.
|
|
composer
binary.This will run our Ballerina program, binding it to local interfaces on port 9090
. If we curl
that socket on the /hello/sayHello
path we’ll see:
|
|
Let’s create a more sophisticated example.
Fortunes favor
We’re going to create a new service with two API resources that add and return fortunes.
We’re going to create a new project for this service. Ballerina has an init
mode to create a new project.
|
|
This will create a new Ballerina project with a template Hello world
service, configuration file Ballerina.toml
, and a .gitignore
file.
git init
in the directory to initialize a repository.The Ballerina.toml
file contains some metadata for your project.
|
|
The org-name
defaults to your username. Ballerina uses the org-name
as namespacing. Ballerina programs can be packaged, shared via the Ballerina Central site and import
-ed into your programs. This is how you can add third-party APIs for which someone has created a package, for example adding support for the Twitter API.
Ballerina comes with a search
sub-command to find packages you might be interested in on Central.
|
|
Now let’s create our service.
Creating a fortunes service
We’re going to delete the template hello_service.bal
file that has been auto-created.
|
|
And create a new service in a file called fortunes_service.bal
|
|
And then populate this file with the following code.
|
|
You can see we’ve created an expanded and extended version of our original hello world service. We’ve again created an HTTP endpoint listening on port 9090
.
We’ve also created an array of strings, originally named fortunes
, to hold our fortunes. When we add fortunes we’ll append to this array, but as we have no permanent data store, new fortunes will vanish when Ballerina stops running the service.
We’ve next created our service, prefixing it with:
|
|
The @http
is an annotation on the HTTP package and defines some metadata for the service that follows, here the base path /fortunes
. Annotations exist in several packages and can be attached to services, resources, functions and a variety of other items.
We’ve then created a service and bound it to our listener. Inside the service we’ve created two resources, again prefixed with annotations that configure the verb and the path our resources are available on. We have resources for GET
and POST
, both on the base path of /fortunes/
.
Each resource has a method, getFortune
and addFortune
respectively, which get all our fortunes as a JSON response and adds a fortune.
Let’s run our program now.
|
|
Let’s start by curl
our existing fortunes.
|
|
And we’ve returned our fortunes as a JSON array. We can also add a fortune.
|
|
We’ve added our fortune as a string but we could easily change the program to take JSON or XML input instead. Now if we return the list of fortunes again,
|
|
And now we can see our added fortune!
Deploying fortunes
Running the ballerina
binary to run the service each time isn’t manageable as a deployment. So Ballerina comes with a number of ways to deploy programs. The easiest way though is to turn our Ballerina program into a Docker image. To do this we can enable Docker support inside our Ballerina program.
ballerina build
programs. Also available is support for command line configuration options using the ballerina/config
package.Let’s do that now:
|
|
You can see we’ve added a new package, ballerinax/docker
, to our list of imports.
x
at the end there, thinking that is a typo has thrown me a couple of times.This package contains support for working with Docker images. We’ve next created an annotation to configure our Docker image, specify a registry (which equates to a Docker registry), a name for the image and any image tags. We also set another annotation to expose our listener port. Ballerina’s Docker support provides many of the configuration options you’re used to when building your own Docker images
Now, if we run the ballerina build
command, Ballerina will make use of the Docker configuration and generate an image.
|
|
The fortunes_service.bal
will be compiled into an executable and then embedded into a Docker image, in our case jamtur01/fortunes:v1.0
.
We can then run that Docker image using the docker
binary.
|
|
And then we can see the container running.
|
|
And add a fortune.
|
|
We could then push this image to a registry and deploy it somewhere. This build process allows you to pretty easily integrate Ballerina into your CI/CD pipeline without a lot of additional configuration or overhead required.
Want to learn more?
I hope that’s been a useful introduction to Ballerina. If you’re interested in learning a bit more about Ballerina, they have some of the best documentation I’ve seen in a young project. Their Learn section has some excellent resources and the Help section can connect you with other folks working with Ballerina.