Ballerina for developing back-ends for modern front-ends
Web apps and mobile apps are the face of digital organizations. Therefore, back-ends powering these apps need to be highly secure, scalable, and responsive. In addition, such back-ends need to aggregate data from multiple services, transform data as necessary, and transmit those to front-ends over required protocols.
Ballerina's support for various protocols, availability of connectors, built-in security features, and advanced data transformation capabilities make it the ideal choice for developing back-ends for modern front-ends.
Simplify back-end development with native REST features
Ballerina provides REST features like path/query parameters, HTTP headers, status codes, and complex JSON structures as first-class citizens within the language itself.
service /sales on new http:Listener(9090) {
resource function get orders() returns Order[] {
return orders.toArray();
};
resource function get orders/[string id]() returns Order|http:NotFound {
return orders[id] ?: <http:NotFound>{
body: string `Order not found. Order ID: ${id}`
};
};
resource function post orders(Order orderRequest)
returns Order|http:BadRequest {
if orders.hasKey(orderRequest.id) {
return <http:BadRequest>{
body: string `Order id already exists.`
};
}
orders.add(orderRequest);
return orderRequest;
}
}
Streamline back-end data handling
Ballerina has built-in support for multi-part payloads, constraint validations, transformations, and, enrichments for working with complex payloads.
service /crm on new http:Listener(9090) {
resource function get customers/[string customerId]/agreement()
returns byte[]|http:InternalServerError {
byte[]|error agreementForm = getAgreementForm(customerId);
if agreementForm is error {
return http:INTERNAL_SERVER_ERROR;
}
return agreementForm;
}
resource function post customers(http:Request request)
returns http:Created|http:InternalServerError {
do {
mime:Entity[] bodyParts = check request.getBodyParts();
string formData = check bodyParts[0].getText();
CustomerData data = check formData.fromJsonStringWithType();
byte[] image = check bodyParts[1].getByteArray();
byte[] agreemntForm = check bodyParts[2].getByteArray();
check registerCustomer(data, agreemntForm, image);
return http:CREATED;
} on fail {
return http:INTERNAL_SERVER_ERROR;
}
}
}
Validate payload constraints in web back-ends
Ballerina's inherent JSON capabilities enable the direct mapping of JSON data into Ballerina records, incorporating functionalities such as constraint validations.
type Order record {|
readonly string id;
@constraint:String {
pattern: {
value: re `C-[0-9]{3}`,
message: "Customer id should be in the format C-XXX"
}
}
string customerId;
string? shipId;
string date;
OrderStatus status;
@constraint:Int {
minValue: {value: 1, message: "Quantity should be greater than one"},
maxValue: {value: 10, message: "Quantity should not exceed 10"}
}
int quantity;
string item;
|};
service /sales on new http:Listener(9090) {
resource function post orders(Order orderRequest)
returns Order|http:BadRequest {
if orders.hasKey(orderRequest.id) {
return <http:BadRequest>{
body: string `Order id already exists.`
};
}
orders.add(orderRequest);
return orderRequest;
}
}
Efficiently expose complex data with GraphQL
With Ballerina's built-in GraphQL functionality, developers can simply expose Ballerina records via GraphQL services, facilitating querying and selectively fetching complex data structures.
type Order record {|
readonly string id;
string customerId;
string? shipId;
Address? shippingAddress;
string date;
OrderStatus status;
int quantity;
string item;
|};
type Address record {|
string number;
string street;
string city;
string state;
|};
service /sales on new graphql:Listener(9090) {
resource function get orders(string? customerId) returns Order[] {
if customerId is () {
return orders.toArray();
}
return from Order entry in orders
where entry.customerId == customerId
select entry;
}
}
Expose real-time data to front-ends via webSockets
Ballerina allows real-time data to be streamed just by implementing a service. This comes with enterprise-ready security features like TLS, mutual TLS, and OAuth2.
service /logistics on new websocket:Listener(9091) {
resource function get vehicles/[string vehicleId]()
returns websocket:Service {
return new LocationService(vehicleId);
}
}
distinct service class LocationService {
*websocket:Service;
remote function onOpen(websocket:Caller caller) returns error? {
// Create a new strand and allocate it to send the locations
_ = start self.routeLocationFromServerToClient(caller, self.vehicleId);
return;
}
function routeLocationFromServerToClient(
websocket:Caller caller, string vehicleId) returns error? {
while true {
Location currentLocation = {
latitude: check random:createIntInRange(668700, 1246700) * 1.0
/ 10000.0,
longitude: check random:createIntInRange(258400, 493800) * 1.0
/ 10000.0
};
check caller->writeMessage(currentLocation);
runtime:sleep(3);
}
}
}
Simplify user authentications and authorizations
Ballerina apps can be seamlessly integrated with any OAuth2-compatible identity provider using a simple set of annotations.
@http:ServiceConfig {
auth: [
{
jwtValidatorConfig: {
issuer: issuer,
audience: audience,
signatureConfig: {
jwksConfig: {
url: jwksUrl
}
}
},
scopes: ["order_insert", "order_read", "cargo_insert", "cargo_read"]
}
]
}
service /sales on new http:Listener(9090) {
@http:ResourceConfig {
auth: {
scopes: ["order_insert"]
}
}
resource function post orders(Order orders) returns http:Ok {
orderTable.add(orders);
return <http:Ok>{body: {message: "Order submitted successfully"}};
};
}
Comprehensive security for web back-ends
Ballerina-based back-ends can simplify securing sensitive data during transit, validating server identity, managing CORS, and enforcing message type restrictions via annotations.
@http:ServiceConfig {
cors: {
allowOrigins: ["http://localhost:3000", "http://www.hmart.com"],
allowHeaders: ["REQUEST_ID"],
exposeHeaders: ["RESPONSE_ID"],
maxAge: 84900
}
}
service /sales on new http:Listener(9090,
secureSocket = {
key: {
certFile: "../path/to/cert",
keyFile: "../path/to/private-key"
}
}
) {
@http:ResourceConfig {
cors: {
allowOrigins: ["http://localhost:3000", "http://www.hmart.com"],
allowCredentials: true
}
}
resource function post orders(Order orderEntry) returns http:Ok {
orderTable.add(orderEntry);
return <http:Ok>{body: {message: "Order submitted successfully"}};
};
}
Securely interact with internal/external services
Ballerina back-ends can securely call services with the necessary security features such as client-side OAuth2, mutual TLS, and JWT-encapsulated user data.
service /logistics on new http:Listener(9090) {
resource function post cargos(Cargo cargo)
returns http:Ok|http:InternalServerError {
cargoTable.add(cargo);
http:Client serviceClient = getServiceClient(cargo.cargoType);
http:Response|error res = serviceClient->post("/shipments", cargo);
if res is http:Response && res.statusCode == 202 {
return <http:Ok>{body: "Successfully submitted the shipment"};
}
return <http:InternalServerError>{
body: {message: "Error occurred while submitting the shipment"}
};
};
resource function get cargos() returns Cargo[] {
return cargoTable.toArray();
};
}
Automate data access with Ballerina
Ballerina's persistence features offer a straightforward way to create a data access layer for any complex application by providing a simplified interface for CRUD operations.
Client dbClient = check new ();
service /sales on new http:Listener(9090) {
resource function get orders() returns Order[]|error {
return from Order entry in orderDatabase->/orders(Order)
select entry;
};
resource function post orders(Order orderEntry)
returns http:Ok|http:InternalServerError|http:BadRequest {
orderEntry.cargoId = getCargoId();
string[]|persist:Error result = orderDatabase->/orders.post([orderEntry]);
if result is string[] {
return http:OK;
}
if result is persist:ConstraintViolationError {
return <http:BadRequest>{
body: {message: string `Invalid cargo id: ${orderEntry.cargoId}`}
};
}
return http:INTERNAL_SERVER_ERROR;
};
}
Develop flexible & scalable back-ends
Ballerina-based back-ends offer the flexibility to manage varying loads. Components can be deployed and scaled independently, providing flexibility and scalability, particularly on platforms like Kubernetes.
In addition, Ballerina back-ends can be instantly taken to production by deploying in Choreo, a comprehensive app development platform with pre-built CI/CD pipelines, multi-environment support, and robust monitoring for Ballerina apps.