Learning Ballerina

Ballerina is an open-source programming language for the cloud that makes it easier to use, combine, and create network services.

This is a collection of code examples that illustrate various Ballerina concepts and functionalities.

Let’s begin learning Ballerina.

Language Basics

How-to Code snippets

Use Cases

Articles

Videos and Talks

Introduction to Ballerina

What is Ballerina?

  • Ballerina is a general-purpose, open-source programming language.
  • It specializes in solving integration/API integration/network interactions & cloud-based problems by providing the right level of language abstractions and tools.
  • Ballerina is NOT a JVM language.
  • Ballerina is a programming language which is
    • compiled : (Compiled languages are converted directly into machine code that the processor can execute. C, C++, Rust, Go are examples. In interpreted Languages, interpreters run through a program line by line and execute each command. PHP, Ruby, Python are examples. )
    • type safe :
    • concurrent :
  • Ballerina is neither an object oriented language nor a functional one.
    • While the language has objects, the development methods are not completely OOP-based.
    • Both objects and functions are first class concepts, giving developers the ability to choose the best option based on their requirements.
  • It has two implementations.
    • jBallerina - written in java to run on top of JVM. This is the first implementation to prove the language.
    • nBallerina - Compiled using LLVM, directly to the platform architecture.

What are the unique features of Ballerina?

  • Data-oriented - Ballerina makes it easy for the user to transfer, declare, manipulate, process and query the data.
  • Network oriented - Ballerina has language constructs/concepts which capture network primitives. (E.g., type system concepts to easily bind data coming via network to language types, service types, client types, etc.)
  • Graphical representation - Can easily explain network interactions via sequence diagrams. Each program has both a textual syntax and an equivalent graphical form based on sequence diagrams
  • Enterprise-grade application development - Built-in concurrency, Explicit error handling, transactions, full life cycle management concepts including testing, documenting, configurability, dependency management & versioning
  • Cloud-Native - Reduces the gap between development to deployment and makes it easier for the developer to write the application and instruct how it should be deployed in the cloud.

Hello World

Main Program

Traditionally the first program we write in any programming language is called a Hello World program – a program that simply outputs the text Hello World to the terminal.

Let's write our first program using Ballerina. Use bal run <file_name> to run the samples.

Sample

Run in Playground

//Binds prefix `io` to `ballerina/io` module. 
import ballerina/io;

//The main function is the program entry point.
public function main() {
    // Print text to the console using `io` module `println` function. 
    io:println("Hello World");
}

Output

Hello World

Service

The network constructs in the language make it easy to develop network interactions easily. A simple HTTP service is as follows.

Sample

import ballerina/http;

service / on new http:Listener(9090) {

    // Handles HTTP GET requests.
    resource function get hello() returns string {
        return "Hello World!";
    }

}

Once the above service is started, invoke the service using the cURL command below using another terminal.

curl http://localhost:9090/hello

Output

Hello World!

Type System

Following are some fundamentnals and details about Ballerina type system.

Type System Fundamentals

Ballerina is a statically typed language.

That means that every variable has a type that is known at compile time.

Ballerina has structural type system.

A static type checker uses either the names or the structure of the types in order to compare them against other types. Checking against the name is nominal typing and checking against the structure is structural typing.

So in Ballerina, type compatibility is identified by considering the structure of the value rather than just relying on the name of the type.

All mainstream object-oriented languages use nominal subtyping. For ex: Java has no structural subtyping at all; all subtype relationships must be explicitly declared.

Ballerina has semantic subtyping.

The syntax - describes which strings of of characters comprise a valid program. i.e., the form of the language.

The semantics - describes what syntactically valid programs mean, what they do. i.e. the meaning of the language.

Semantic subtyping is an approach for defining subtyping relation based on set-theoretic models, rather than syntactic rules.

Type is a set of values. When is an assignment, x = y legal?

There are at least two possible answers:

  1. When x and y are of equal types

    Structural type checking decides that.

  2. When y’s type can be “converted” to x’s type

    • Once we consider types as set of values, we realize immediately one set may be a subset of another set.
    • When the set of values in one type, T, is a subset of the set of values in another type, U, we say that T is a subtype of U.
    • There are two main approaches for defining the subtyping relation:
      • syntactic approach - it is defined by means of a formal system of deduction rules
      • semantic approach - It is defined based on set theories. The subtyping relation is defined as inclusion of sets denoting types. Ballerina is using this approach.

Semantic subtyping is most useful for expressing types of tree-structured data, such as JSON.

Ballerina has network aware type system.

Ballerina’s type system is specifically focused on aiding the development of networked and distributed applications. It has constructs that seamlessly map to network programming concepts such as services and network resources.

Langlib Functions

This is a library which provides fundamentnal operations on built-in datatypes. There is a ballerina/lang.T module for each built-in type T and they are automatically imported using T prefix.

We can call Langlib functions in two ways and both produce the same result.

Currently there is Langlib module for following built-in types.

  • For Simple Types - lang.boolean, lang.int, lang.float, lang.decimal
  • For Structured Types - lang.array, lang.map, lang.table
  • For Sequence Types - lang.string, lang.xml
  • For Behavioral Types - lang.object, lang.error, lang.stream, lang.future, lang.typedesc
  • For common types, provides functions that work on values of more than one basic type - lang.value

Other than above modules for built-in types Langlib has following modules as well.

  • For functions related to language runtime - lang.runtime
  • For functions related to transactions - lang.transaction

Langlib functions can be called in two different ways.

Using method call syntax

import ballerina/io;

public function main() {
    // Can call using the conventinent method-call syntax.
    string s = "hello World".substring(1, 2);

    io:println(s);
}

Using as a module function

import ballerina/io;

public function main() {
    // Can call as a langlib module function. No need to import explicitly. 
    string s = string:substring("Hello World", 1, 2);

    io:println(s);
}

Values and Types in Ballerina

Ballerina programs operate on a rich universe of values. This universe of values is partitioned into a number of basic types; every value belongs to exactly one basic type.

A variable has a type, which constrains what values the variable can hold.

Values are of four kinds, each corresponding to a kind of basic type:

  1. Simple values - which are not constructed from other values;
    • nil, boolean, int, float, decimal
  2. Structured values - which contain other values. They are containers for other values, which are called their members.
    • array, map, record, table, tuple
  3. Sequence values - which consists of an ordered sequence of zero or more constituent items, where the constituent items belong to the same basic type as the sequence value itself.
    • string, xml
  4. Behavioral values- which are not just data
    • function, object, error, stream, future, typedesc
  5. Other - which allows some flexibility to define custom types based on the combination of two or more types.
    • any, anydata, json, byte and union

Let's learn the Ballerina types in detail.

Simple Basic Types

Ballerina supports following simple basic types.

Integer

  • The type int is an integer data type in a 64 bit signed representation. (i.e. signed integers than can fit into 64 bits using a two's complement representation). This is similar to long in Java.
  • Range: -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807
  • Integer overflow results in a runtime error.
  • The integer literals can be declared either in decimal or hexadecimal format (not octal).
  • Supported Operators
    • Basic arithmetic operators : + ,- , * , /, and %
    • Comparison operators : == , !=, < , >, <=, >=
    • Compound assignment operations : += & -=
    • Bitwise operators : &, | , ^ , ~ , << and >>

Sample:

import ballerina/io;

public function main() {
    //Integer literal in decimal.
    int m = 100;
    io:println(m);

    // Integer literal in hexadecimal.
    int n = 0xFFFF;
    io:println(n);

    // Use compound assignment operation`+=`. 
    n += m;
    io:println(n);
}

Output

100
65535
65635

Float

  • The float type corresponds to IEEE 754-2008 64-bit binary (radix 2) floating point numbers.
  • This is same as double in Java.
  • No implicit conversions between int and float are allowed. Need to use <T> for explicit conversions.
  • Literal uses the suffix f.

Sample

import ballerina/io;

public function main() {
    float f1 = 1.67;

    int i1 = 5;

    // Use `<T>` for explicit conversions.
    float f2 = f1 + <float>i1;
    io:println(f2);

    //Use ballerina/lang.float function.
    float f3 =f1.round();
    io:println(f3);   
}

Output

6.67
2.0

Decimal

  • The decimal type corresponds to a subset of IEEE 754-2008 128-bit decimal (radix 10) floating point numbers.
  • Represents decimal fractions exactly by preserving precision.
  • Literal uses the suffix d.

Sample

import ballerina/io;

public function main() {
    //Float doesn't represent decimal fractions exactly.
    float f = 100.10 - 0.01;
    io:println(f);

    //Decimal represents decimal fractions exactly.
    decimal d = 100.10 - 0.01;
    io:println(d);
}

Output

100.08999999999999
100.09  

Boolean

  • The boolean type has two values: true, false.
  • The logical operators, ! , || and && are supported with boolean types.
  • The || and && operators support the same short-circuit behavior as in C.

Sample

Run in Playground

import ballerina/io;

public function main() {
    boolean b1 = true;
    boolean b2 = false;

    // Logical operators with boolean.
    boolean b3 = !b1;
    io:println(b3);

    boolean b4 = b1 && b2;
    io:println(b4);

    boolean b5 = b1 || b2;
    io:println(b5);
}

Output

false
false
true

Nil

  • The nil type contains a single value, called nil, which is used to represent the absence of any other value.
  • This is the only basic type which consists of a single type.
  • The nil value is written as ().
  • The nil value can also be written null, for compatibility with JSON; the use of null should be restricted to JSON-related contexts.
  • Any function that ends without a return statement implicitly returns nil.
  • To declare the value of any type T as nil : T? x = ();

Elvis Operator

  • The Elvis Operator is represented by a question mark followed by a colon: ?:

    first operand ?: second operand
    
  • If first operand isn't nil, then it will be returned. If it is nil, then the second operand will be returned.

Sample

import ballerina/io;

public function main() {

    int? i = ();
    io:println(i);

    json j = null;
    io:println(j);

    //Elvis Operator
    int k = i ?: 0;
    io:println(k);
}

Output



0

Sequence Types

Ballerina supports follwoing sequence types.

String

  • A string is an sequence of zero or more Unicode characters.
  • A string may include Unicode noncharacters, such as 0xFFFE and 0xFFFF.
  • The string basic type is inherently immutable.
  • A character is represented by a string of length 1.
  • There is a built-in subtype string:Char for single character strings.
  • The [i] expression on a string value points to the character at index i. The index positions on the string type starting with zero.
  • Supported operators
    • Equality Check - == operator : checks for the same characters.
    • Comparison operators - < , <=, =>, > : compalre code points.
    • Concatenation - + operator

NOTE:

  • Unicode is an encoding for textual characters which is able to represent characters from many different languages.
  • Each character is represented by a unicode code point.
  • A code point is an integer value that uniquely identifies the given character.
  • Unicode characters can be encoded using different encodings, like UTF-8 or UTF-16.
  • These encodings specify how each character's Unicode code point is encoded, as one or more bytes.

Sample

import ballerina/io;

public function main() {
    //Enclosed within double quotes
    string s = "Hello World";
    io:println(s);

    //Index based access
    io:println(s[4]);

    // Unicode code point using one or more hex digits `\u{H}`.
    string symbol = "\u{1F483}";
    io:println(symbol);

    string:Char c = "!";
    io:println(c);
    
    string greeting = s + c;
    io:println(greeting);
}

Output

Hello World
o
💃
!
Hello World!

String Template Literal

  • String templates allows to embed expressions of simple basic types (except nil).

Sample

import ballerina/io;

public function main() {
    string name = "Ballerina";
    int count = 2;

    // Create a `string` template embedding the `name` variable.
    string template = string `Hello ${name}, count is ${count} !!!`;

    // Print the defined string value.
    io:println(template);
}

Output

Hello Ballerina, count is 2 !!!

XML

Structured Types

Ballerina supports following structured types.

Lists

  • A list value is a container that keeps its members in an ordered list.
  • The number of members of the list is called the length of the list.
  • The key for a member of a list is the integer index representing its position in the list, with the index of the first member being 0.
  • A list is iterable.
  • Ballerina supports following lists types.

Array

  • Arrays are sequential data structures consisting of values of the same type. T[] is an array of T.
  • Arrays are mutable.
  • Can access the individual elements of this array using the [i] notation. Array indexing starts with zero.
  • The comparison operators - == or != : perform a deep comparison. The two arrays are equal if they have the same members in the same order.
  • Unless the length is specified or inferred from the value, arrays are unbounded by length. That means they can grow up to any length based on the given index.

Sample

import ballerina/io;

public function main() {
    //Creates an int array by specifiying an array literal. 
    //Length is not speicfied, so this will create unbounded array
    int[] v = [1, 2, 3];
    io:println(v);
    io:println("Length of array v:", v.length());

    //Arrays can grow up to any length based on the given index. 
    v[9] = 10;
    io:println(v);
    io:println("Length of array v:", v.length());

    //Length is specified when creating,so  it can have only 3 elements maximum. 
    //If we try to store more than 3 elements it gives errors.
    int[3] v2 = [1,2,3];
    io:println(v2);

    //Access elements by index.
    io:println(v2[1]);

    //Multi dimensional array. 
    int[][] v3 = [[1, 2, 3], [10, 20, 30], [5, 6, 7]];
    io:println(v3);
    io:println(v3.length());

    //The `*` is used to infer the array size from array literal. 
    //Cannot assing more elements than the initial size. 
    int[*] v4 = [1, 2, 3, 4];
    io:println(v4.length());   
}

Output

[1,2,3]
Length of array v:3
[1,2,3,0,0,0,0,0,0,10]
Length of array v:10
[1,2,3]
2
[[1,2,3],[10,20,30],[5,6,7]]
3
4

Tuple

  • Creates a list of values just like arrays.
  • The main difference between arrays and tuples is that an array has only one type applicable to every member of its list, whereas in a tuple type you can individually specify the types for each member.

Sample

import ballerina/io;

public function main() {

    // Defines the type of `a` as a pair that consists of an `int` and a `string`.
    [int, string] t = [10, "John"];
    io:println(t);
    
    // Tuple destructuring:The assignment statement assigns values of the 
    // tuple on the right to the variables on the left.
    int i;
    string s;
    [i, s] = t;
    io:println(i);
    io:println(s);

    // Invokes a function that returns a tuple.
    var [q, r] = divideBy10(6);
    io:println("06/10: ", "quotient=", q, " remainder=", r);

    // To ignore a value in a tuple, use `_`.
    var [q1, _] = divideBy10(57);
    io:println("57/10: ", "quotient=", q1);
}

// This function returns a tuple of two integers.
function divideBy10(int d) returns ([int, int]) {
    int q = d / 10;
    int r = d % 10;
    return [q, r];
}

Output

[10,"John"]
10
John
06/10: quotient=0 remainder=6
57/10: quotient=5

Mappings

  • A mapping value is a container where each member has a key, which is a string, that uniquely identifies within the mapping.
  • We use the term field to mean the member together its key; the name of the field is the key, and the value of the field is that value of the member.
  • No two fields in a mapping value can have the same name.
  • A mapping is iterable.
  • Ballerina supports following mapping types.

Map

  • Defined as map<T> where T is the map's constraint type.
  • It is a mapping from keys (that are strings) to values of the type specified as the map’s constraint type.
  • The comparison operators - == or != : perform a deep comparison. The two maps are equal if they have the same set of keys and the values for each key are equal.
  • Maps are mutable, and m["x"] will return the integer value stored in "x", or nil if missing.

Sample

import ballerina/io;

public function main() {
    string computedKey = "a" + "b";

    // Creates a `map` constrained by the type `int`.
    // Computed keys, where the value of the key expression evaluated at the runtime 
    // can be used within brackets. 
    map<int> m = {
        "x": 100,
        "y": 200,
        [computedKey] : 500
    };
    io:println(m);

    // Adds a new entry for `z`.
    m["z"] = 500;
    io:println(m);

    // Gets the entry for `x`.
    int? v = m["x"];
    io:println(v);

    //Gets an entry for non existing key.
    v = m["a"];
    io:println(v);

     // Gets value using Langlib function.
     int i = m.get("x");
     io:println(i);

    //Get the list of keys.
    string[] keys = m.keys();
    io:println(keys);
}

Output

{"x":100,"y":200,"ab":500}
{"x":100,"y":200,"ab":500,"z":500}
100

100
["x","y","ab","z"]

Record

  • A record is a collection of fields of a specific type.
  • With record types, you have control over what your keys are.
  • The keys are named and their types define the types of values that are allowed for the fields.
  • Records are mutable.
  • Typically a record type is combined with a type definition.
  • The name of the type is not significant: a record is just a collection of fields.
  • Record equality works same as map equality.

Annonymous record

  • Record types can be defined in-line as well. These types do not have a type name associated with them. So the record descriptor itself has to be specified when declaring variables of an annonymous record type.
  • Anonymous record types can be used in instances where there is no need to refer to the record type by its name (e.g., records as record or object fields, records as function parameters).

Sample

import ballerina/io;

// Defines a record type named `Student`.
type Student record {
    int age;
    string name;
};

// Defines a record type named `Person` with an annonymous record inside.
type Person record {
    int age;
    string name;
    record {
        string city;
        string country;
    } address;
};

public function main() {
    // Creates a `annonymous record`, specifying values for its fields.
    record { int age; string name; } r1 = {
        age: 10,
        name: "Anne"
    };
    io:println(r1);

    // Creates a `Student` record.
    Student s1 = {
        age: 10,
        name: "Anne"
    };
    io:println(s1);

    //Check whether the two records are equal.
    io:println(r1 == s1);

    //Access fields
    int r1Age = r1.age;
    io:println(r1Age);

    Person p = {
        age: 33,
        name: "John",
        address: {
            city: "Colombo",
            country: "LK"
        }
    };
    io:println(p);
}

Output

{"age":10,"name":"Anne"}
{"age":10,"name":"Anne"}
true
10
{"age":33,"name":"John","address":{"city":"Colombo","country":"LK"}}

Open vs Closed Records

  • Closed Record :
    • If the set of fields is fixed. The {| and |} delimiters indicate that the record type allows mapping values, which contain only the described fields.
    • map<T> is same as record {| T...; |}.
  • Open Record :
    • If the set of fields is not fixed. The { and } delimiters indicate that in addition to the defined fields, this record type allows additional fields with anydata values. The descriptor record { } is equivalent to record {| anydata...; |}.
    • Record types are by default open: they allow fields other than those specified.
    • The type of unspecified fields are anydata. Records are maps. Open records belongs to map.
    • Use quoted keys for fields not mentioned in the record type.

Sample

import ballerina/io;

// `Person` type is open record allows additional fields with `anydata` values.
type Person record {
    string name;
    int age;
};

// `Employee` type is a closed record. 
type Employee record {|
    string name;
    int age;
    int id;
|};

// Define an open record with rest fields. All additional fields should be of the type or 
// the subtype of the rest field.
type Grades record {|
    int maths;
    int english;
    int science;
    string...;
|};

public function main() {
    // Create an `Employee` value
    Employee e = {
        name: "Anne",
        age: 34,
        id: 567
    };
    io:println(e);

    // Create an `Person` value 
    Person p1 = {
        name: "John",
        age: 33
    };
    io:println(p1);

    // Create an `Person` value with additional filed.
    Person p2 = {
        name: "Mark",
        age: 34,
        "city": "London"
    };
    io:println(p2);

    // You can assign an `Employee` type value to a `Person`.
    Person p3 = e;
    io:println(p3);

    // You can assign record value to a `map`.
    map<anydata> m = e;
    io:println(m);

    // Create a record value with additional rest fields
    Grades g = {
        maths: 55,
        english: 98,
        science: 43,
        "art": "A",
        "music": "B"
    };
    io:println(g);
    
    // Can assign record to a map of corresponding type.
    map<int|string> m2 = g;
    io:println(m2);
}

Output

{"name":"Anne","age":34,"id":567}
{"name":"John","age":33}
{"name":"Mark","age":34,"city":"London"}
{"name":"Anne","age":34,"id":567}
{"name":"Anne","age":34,"id":567}
{"maths":55,"english":98,"science":43,"art":"A","music":"B"}
{"maths":55,"english":98,"science":43,"art":"A","music":"B"}

Optional Fileds

  • Fields of a record can be marked as optional.
  • These fields can be omitted when creating a record.
  • Such fields can be accessed via optional field access (e.g., p?.name) or member access (e.g., p[“name”]) which will both return () if the field is not present in the record.

Sample

import ballerina/io;

type Headers record {
    string 'from; // When a keyword is using as field name a single quote is used. This is a required field.
    string to = "test@example.com"; // This is a field with defualt value.
    string subject?; //This is an optional field.
};

public function main() {

    Headers h1 = {
        'from: "John"
    };
    io:println(h1); // Will have values for default field too.

    //Use ?. operator to access optional field
    string? subject = h1?.subject;
    io:println(subject); // Subject is nil here.

    Headers h2 = {
        'from: "John",
        to: "anne@exmample.com",
        subject: "test"
    };
    io:println(h2);

    // Can access the fields as follows.
    io:println(h1["subject"]);
    io:println(h2["subject"]);
}

Output

{"from":"John","to":"test@example.com"}

{"from":"John","to":"anne@exmample.com","subject":"test"}

test

Record Type Reference

  • Record type referencing provides a convenient way to copy the fields defined in a record type descriptor to another record type descriptor.
  • It is equivalent to explicitly defining those fields in the new type descriptor.
  • Type referencing copies the fields including their properties (e.g., type, default value, optional status).
  • A record type descriptor can have multiple type references as well as chained-type references.

Sample

import ballerina/io;

type Person record {|
    string name;
    int age = 25;
|};

type Student record {
    *Person;  // References the `Person` record.
    string studentId;
};

type Grades record {|
    int maths;
    int english;
    int science;
|};

public function main() {
    Person p = {name: "John", age: 13};
    io:println(p);

    Student s1 = {name: "Jane", studentId: "2103X"};
    io:println(s1);

    Grades g = {
        maths: 32,
        english: 34,
        science: 54
    };
    // When a spread field is specified, all the fields of the relevant
    // mapping value are added to the new record value being created.
    Student s2 = {name: "alex", studentId: "4224C", ...g };
    io:println(s2);
}

Output

{"name":"John","age":13}
{"studentId":"2103X","name":"Jane","age":25}
{"studentId":"4224C","name":"alex","age":25,"maths":32,"english":34,"science":54}

Table

  • A table is a collection of records. Each record represents a row of the table.
  • The tables also preserve the order of the rows.
  • You can either iterate over the table, item by item, like arrays, or directly point to the item using the associated key.
  • The rows are identified by keys.
    • table stores the keys as fields in the rows (Similar to SQL based database table where one of the columns is designated as a primary key)
    • Key is not limited to string type. You can also have keys belonging to any subtype of plain data, which also includes structured types.
    • You can have a table that has a multiple part key spread over several fields.
    • Key is immutable. So have to mark as readonly when defining it. A value cannot be assigned to such a field after the record is created.

Sample

import ballerina/io;

type Student record {
    readonly string name; // This will be used as key
    int age;
};

type Employee record {
    readonly string name;
    readonly int id;
    string country;
    decimal salary;
};

type Manager record {
    readonly record {
        string name;
        int id;
    } identity;
    string country;
    decimal salary;
};

public function main() {
    //Table with string key
    table<Student> key(name) t1 = table [
            {name: "John", age: 11},
            {name: "Jane", age: 12}
        ];
    io:println(t1);

    Student? s = t1["John"];
    io:println(s.toString());

    Student s2 = {name: "Anne", age: 21};
    t1.add(s2);
    io:println(t1);

    //Table with multiple key fields
    table<Employee> key(name, id) t2 = table [
            {name: "Mike", id: 1239, country: "UK", salary: 435},
            {name: "Mike", id: 4323, country: "US", salary: 510}
        ];
    io:println(t2);

    //Table with structured key
    table<Manager> key(identity) t3 = table [
            {
                identity: {
                    name: "Mike",
                    id: 1239
                },
                country: "LK",
                salary: 453
            },
            {
                identity: {
                    name: "Mike",
                    id: 2342
                },
                country: "LK",
                salary: 453
            }
        ];

        Manager m = t3.get({name:"Mike", id: 1239});
        io:println(m);
}

Output

[{"name":"John","age":11},{"name":"Jane","age":12}]
{"name":"John","age":11}
[{"name":"John","age":11},{"name":"Jane","age":12},{"name":"Anne","age":21}]
[{"name":"Mike","id":1239,"country":"UK","salary":435},{"name":"Mike","id":4323,"country":"US","salary":510}]
{"identity":{"name":"Mike","id":1239},"country":"LK","salary":453}

Behavioral Types

Ballerina supports following behavioral types.

Object

Why Objects?

  • Object encapsulates data with functions that operate on the data.

Introduction

  • An object consists of named members,
    • field - stores a value
    • method - a function that can be invoked on the object
  • When a method is invoked on an object, the function can access the object using the self variable.
  • It is not possible for an object to have a field and a method with the same name.
  • Need to use the new operator with a class to get an object.
  • There are three visibility levels.
    • private - only visible within the object and its functions.
    • public - visible to any module.
    • protected - visible only within the same module.

Init Function

  • The init method in the class initializes the object.
  • Arguments to new are passed as arguments to init.
  • The return type of init must be a subtype of error?.
    • If init returns (): The new returns the newly constructed object.
    • If init returns an error : The new returns that error.
    • If init does not specify a return type:
      • the return type defaults to ().
      • Then new will never return an error.

Sample

import ballerina/io;

class Customer {
    private string firstName;
    string lastName;
    public int id;

    function init(string firstName = "A", string lastName = "B", int id = 0) {
        self.firstName = firstName;
        self.lastName = lastName;
        self.id = id;
    }

    function getFullName() returns string {
        return self.firstName + " " + self.lastName;
    }
}

public function main() {
    //Calls init function with all parameters
    Customer cust1 = new ("Anupama", "Pathirage", 10032);
    string fullName = cust1.getFullName(); //Access  a method
    io:println(fullName);

    //Default values are used 
    Customer cust2 = new;
    int id = cust2.id; // Access a public id;
    io:println(id);

    //This inintialization is useful when the type of the object 
    //cannot be determined based on nthe context(e.g., when the LHS is var or union)
    var cust3 = new Customer("Bill", "Gates", 10031);
    string lastName = cust3.lastName;
    io:println(lastName);
}

Output

Anupama Pathirage
0
Gates

error

Other types

Following types do not add to the universe of values. They are just adding new ways to describe subsets of this universe.

Any

  • The any type consisting of all values other than errors. A value belongs to the any type if and only if its basic type is not error.
  • Thus all values belong to the type any|error.
  • Equivalent to a union of all non-error basic types.
  • Langlib lang.value module contains functions that apply to multiple basic types.
  • A variable of type any can be cast to a specific type using the < > symbol enclosure.

Sample

import ballerina/io;

public function main() {
    // Can cast `any` to specific type.
    any x = 1.4;
    float n = <float>x;
    io:println(n);

    if (x is decimal) {
        io:println("x is decimal");
    } else if (x is string) {
        io:println("x is string");
    } else if (x is float) {
        io:println("x is float");
    }
}

Output

1.4
x is float

Anydata

  • This is used to represent plain data.
  • It can be defined as:
    () | boolean | int | float | decimal
    | string | xml
    | anydata[] | map<anydata> | table<map<anydata>>
    
  • It is a subtype of any.
  • The == and != operators on anydata test for deep equality.
  • The equality operation also takes care of cycles within anydata structure values.

Sample

import ballerina/io;
public function main() {
    anydata[] fArr = [1.34, 3.43];
    float f = <float>fArr[0];
    io:println(f);
}

Output

1.34

byte

Json

union

  • A union type describes a value that can be one of several types.
  • Why union types?
    • Union is useful when the program expects a value to be either many different types. e.g., A parameter to the function or return value can be either int or float etc.
    • In traditional object-oriented code, we have to abstract over the two types by creating a hierarchy of types to achieve this.
  • T1|T2|T3 represents a union of the sets T1, T2 and T3.
    • e.g., The type int|float|string can hold either int, float or string.
  • The is operator is used to check whether the value belongs to a specific type. The type will be narrowed with the use of the is operator. Narrowing occurs when Ballerina can deduce a more specific type for a value based on the structure of the code.
  • T1|() can be written as T? as well.

Sample

import ballerina/io;

type Address record {
    string city;
    string country;
    int code;
};

//Instead of writing longer union, it is  easy to  use a  type defintion.
type Location Address|string|int;

public function main() {
    int|string|float u1 = "this is a string";
    if u1 is int {
        io:println("union contains a int  value");
    } else if u1 is float {
        io:println("union contains a float  value");
    } else { //Type narrowed  to  string  here.
        io:println("union contains a string  value");
    }

    //Union types with nil.
    int|() i1 = 4;
    io:println(i1);
    float? f1 = ();
    io:println(f1);

    //Union type with type descriptors
    Location loc = "UK";
    io:println(loc);
}

Output

union contains a string  value
4

UK

Ballerina How To Snippets

This is a collection of Ballerina code snippets for quick reference.

Download a file from URL using Ballerina

This example shows how to download a small file from the web on to your local machine.

Sample

import ballerina/io;
import ballerina/http;

function downloadFile(string filePath, string url) returns error? {
    http:Client httpEP = check new (url);
    http:Response e = check httpEP->get("");
    return io:fileWriteBytes(filePath, check e.getBinaryPayload());
}

public function main() {
    error? status = downloadFile("../img/logo.png", "https://ballerina.io/img/branding/ballerina_logo_dgrey_png.png");
    if (status is error) {
        io:println(status);
    } else {
        io:println("File downloaded successfully");
    }
}

Output

File downloaded successfully

Validate email address using Regex in Ballerina

This example shows how to download a small file from the web on to your local machine.

Sample

import ballerina/io;
import ballerina/regex;

public function main() {
    string email = "hello$example.com";
    io:println(email + " is valid:", isValid(email));
    email = "hello@abc.com";
    io:println(email + " is valid:", isValid(email));
}

function isValid(string email) returns boolean {
    if email.length() < 3 || email.length() > 254 {
        return false;
    }
    return regex:matches(email, "^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$");
}

Output

hello$example.com is valid:false
hello@abc.com is valid:true

Usecases

Ballerina GraphQL with multiple datasources

Source code

The full source code for the scenario can be found in ballerina-scenarios repo.

Sample use case

The data source to the GraphQL server can be anything such as a database, API, or service that holds data. Also, GraphQL can interact with any combination of data sources. In this use case, let’s see how we can implement a GraphQL server using the Ballerina language to expose data in the MySQL database and data retrieved via another API call.

The MySQL database holds data about a book store, and it has book data and author data. Additional information related to Books is retrieved using Google Books API. Clients of the book store can do the following operations via the GraphQL server.

  • Retrieve the details of all the books
  • Retrieve the details of the book by providing the book name
  • Add new books to the database

The information sources for the above operations are as follows.

  • Title, published year, ISBN number, author name, author country - Retrieved from DB
  • Average rating and rating count - Retrieved from Google Books API filtered using ISBN number of the book.
    E.g.: https://www.googleapis.com/books/v1/volumes?q=isbn:9781101042472

GraphQL with Balleirna

Set up the Database

Create the sample MySQL database and populate data with the data.sql script as follows.

mysql -uroot -p < /path/to/data.sql

Run the code

Execute bal run command within the bookstore project folder.

Test the service

To call the GraphQL server, we need to use a client. We can do this from the command line with curl by sending an HTTP POST request to the endpoint, passing the GraphQL query as the query field in a JSON payload. If you prefer to use a graphical user interface, you can use clients such as GraphiQL or Altair.

For all the requests the endpoint is : http://localhost:4000/bookstore

Sample Request 1: Get the titles of all books

GraphQL query:

{allBooks {title}}

Response:

{
 "data": {
   "allBooks": [
     { "title": "Pride and Prejudice" },
     { "title": "Sense and Sensibility" },
     { "title": "Emma" },
     { "title": "War and Peace" },
     { "title": "Anna Karenina" }
   ]
 }
}

CURL command to request the same: curl -X POST -H "Content-type: application/json" -d '{ "query": "{allBooks {title}}" }' 'http://localhost:4000/bookstore'

Sample Request 2: Get more details of all books

This is where the true power of GraphQL comes in. Users can request the exact information they need in the format they prefer without having different endpoints, but just by changing the query.

GraphQL query :

{allBooks {title, author{name}, reviews{ratingsCount, averageRating}}}

Response :

{
 "data": {
   "allBooks": [
     {
       "title": "Pride and Prejudice",
       "author": {
         "name": "Jane Austen"
       },
       "reviews": {
         "ratingsCount": 1,
         "averageRating": 5
       }
     },
     {
       "title": "Sense and Sensibility",
       "author": {
         "name": "Jane Austen"
       },
       "reviews": {
         "ratingsCount": 3,
         "averageRating": 4
       }
     },
     {
       "title": "Emma",
       "author": {
         "name": "Jane Austen"
       },
       "reviews": {
         "ratingsCount": null,
         "averageRating": null
       }
     },
     {
       "title": "War and Peace",
       "author": {
         "name": "Leo Tolstoy"
       },
       "reviews": {
         "ratingsCount": 5,
         "averageRating": 4
       }
     },
     {
       "title": "Anna Karenina",
       "author": {
         "name": "Leo Tolstoy"
       },
       "reviews": {
         "ratingsCount": 1,
         "averageRating": 4
       }
     }
   ]
 }
}

CURL command to send the same request:

curl -X POST -H "Content-type: application/json" -d '{ "query": "{allBooks {title, author{name}, reviews{ratingsCount, averageRating}}}" }' 'http://localhost:4000/bookstore'

Sample Request 3: Get details of books with input parameter

GraphQL Query:

{bookByName(title: "Emma") {title, published_year}}

Response:

{ "data": { "bookByName": [{ "title": "Emma", "published_year": 1815 }] } }

CURL Command to send the same request:

curl -X POST -H "Content-type: application/json" -d '{ "query": "{bookByName(title: \"Emma\") {title, published_year}}" }' 'http://localhost:4000/bookstore'

Sample Request 4: Mutation to insert data into the database

GraphQL Query:

mutation {addBook(authorName: "J. K. Rowling", authorCountry: "United Kingdom", title: "Harry Potter", published_year: 2007, isbn: "9781683836223")}

Response:

{
 "data": {
   "addBook": 6
 }
}

CURL Command to send the same request:

curl -X POST -H "Content-type: application/json" -d '{ "query": "mutation {addBook(authorName: \"J. K. Rowling\", authorCountry: \"United Kingdom\", title: \"Harry Potter\", published_year: 2007, isbn: \"9781683836223\")}" }' 'http://localhost:4000/bookstore'

Ballerina WebSocket for real-time stock updates

Source code

The full source code for the scenario can be found in ballerina-scenarios repo.

Sample Use case

The WebSocket-based stock management server is getting stock data feed via WebSocket-based connections from different exchanges. The client applications will subscribe to the preferred stock symbols by registering those symbols with the stock management server.

When the stock management server receives updates to the subscribed symbols, it will broadcast the data to the subscribed clients using a WebSocket connection.

WebSocket with Balleirna

Running the applications

Let's run the three applications using the below commands in the following order.

bal run ws_stock_mgt_server.bal.

bal run client.bal

bal run exchange.bal

Ballerina HTTP Service for book and movie search

Source code

The full source code for the scenario can be found in ballerina-scenarios repo.

Sample Use case

Refer to the scenario below.

HTTP Service in Balleirna

External Calls

Google Books API

https://www.googleapis.com/books/v1/volumes?q=intitle:war%20and%20peace

IMDB Movie API

https://imdb-api.com/en/API/SearchMovie/k_mhb0408y/war%20and%20peace

https://imdb-api.com/en/API/Trailer/k_mhb0408y/tt3910804

Test URL

curl "localhost:9090/books/search?name=war%20and%20peace" | jq

Articles and Blogs

Videos and talks

Videos

Talks