- Write a RESTful API with Ballerina
- Write a gRPC service with Ballerina
- Write a GraphQL API with Ballerina
- Work with data using queries in Ballerina
- Build a data service in Ballerina
- Build a Change Data Capture (CDC) service in Ballerina
- Work with Large Language Models (LLMs) using natural expressions
- Deploy Ballerina on Kubernetes
- Manage data persistence with bal persist
- Create your first connector with Ballerina
- Write a workflow with Ballerina
- Write a workflow with a human task
- Handle errors and replay failed activities in workflows
Serverless architecture allows developers to focus on writing application logic without worrying about managing servers or infrastructure. AWS Lambda automatically handles provisioning, scaling, and maintaining resources, letting you concentrate solely on your code. Following are the Ballerina functions in a serverless environment,
AWS Lambda
The AWS Lambda extension provides the functionality to write AWS Lambda-compatible packages by exposing a Ballerina function as an AWS Lambda function.
Info: Ballerina functions can be deployed in AWS Lambda by annotating a Ballerina function with
@lambda:Functionadhering to the following function signature:function (lambda:Context, json|EventType) returns json|error
Supported triggers
An AWS Lambda function can be triggered by various AWS services. You can find the list of supported notification types below.
- Direct invocation
- Simple Queue Service (SQS)
- Simple Storage Service (S3)
- DynamoDB
- Simple Email Service (SES)
- API Gateway
- EventBridge
- Simple Notification Service (SNS)
- Kinesis Data Streams
- Function URLs
Each trigger delivers its event in a different payload format. Therefore, the package provides a record type per trigger, which you can use as the parameter type of the function instead of json.
| Trigger | Event record type |
|---|---|
| Direct invocation | json |
| SQS | lambda:SQSEvent |
| S3 | lambda:S3Event |
| DynamoDB | lambda:DynamoDBEvent |
| SES | lambda:SESEvent |
| API Gateway | lambda:APIGatewayProxyRequest |
| EventBridge | lambda:EventBridgeEvent |
| SNS | lambda:SNSEvent |
| Kinesis Data Streams | lambda:KinesisEvent |
| Function URL | lambda:FunctionURLRequest |
Follow the instructions in the sections below to deploy a Ballerina function in AWS Lambda.
Set up the AWS account
Follow the steps below to set up an AWS account and the AWS CLI.
Create a user
Follow the steps below to create a new user in your AWS account.
-
Create a new AWS user.
Note: Enter a username and enable access to the AWS Management Console.
-
Set the permissions to the created user.
Note: Enable
AWSLambda_FullAccessor higher permissions. -
Obtain the access keys.
-
Configure the AWS CLI for the user by providing the access key and secret generated in the user creation.
Create a role
Follow the steps below to create a new role in your AWS account.
-
Create a new role.
-
Set the permissions to the created role.
Info: Enable
AWSLambda_FullAccessor higher permissions. -
Access the newly created role in the AWS Console, and copy the role ARN to use when the Lambda function is being deployed.
Create the function
You can write AWS Lambda functions that use different triggers based on your use case.
Functions annotated as @lambda:Function should always have the first parameter with the lambda:Context object, which contains the information and operations related to the current function execution in AWS Lambda such as the request ID and the remaining execution time.
The second parameter with the json value contains the input request data. This input value format will vary depending on the source, which invoked the function (e.g., an AWS S3 bucket update event). The return type of the function is json. When the function is triggered by the event, the function body executes and it simply logs the input JSON and returns the JSON.
Alternatively, you can declare the second parameter with the event record type of the trigger, in which case the payload is converted to that record and its fields become directly accessible as shown below.
import ballerinax/aws.lambda; @lambda:Function public function notifySNS(lambda:Context ctx, lambda:SNSEvent event) returns json { return event.Records[0].Sns.Message; }
Tip: Some event fields have names that are not valid Ballerina identifiers. For example, the
detail-typefield of an EventBridge event is accessed asevent.detail\-type.
A function can also be invoked over a function URL, which is a dedicated HTTPS endpoint for the function. Function URLs use the API Gateway payload format 2.0. Therefore, declare the parameter as lambda:FunctionURLRequest rather than lambda:APIGatewayProxyRequest, and return a lambda:FunctionURLResponse to control the status code and the headers of the response as shown below.
import ballerinax/aws.lambda; @lambda:Function public function greet(lambda:Context ctx, lambda:FunctionURLRequest request) returns json { lambda:FunctionURLResponse response = { statusCode: 201, headers: {"Content-Type": "application/json"}, body: string `{"path": "${request.rawPath}"}` }; return response.toJson(); }
Note: The compiler validates the signature of every annotated function and fails the build when it does not match, for example, when the function has no parameters, has defaultable or rest parameters, has a first parameter that is not
lambda:Context, or returns an unsupported type. Earlier versions excluded such a function from the generated artifacts without reporting anything.
Info: For examples of creating AWS Lambda functions, see Examples.
Build the function
The AWS Lambda functionality is implemented as a compiler extension. Therefore, artifact generation happens automatically when you build a Ballerina module by executing the command below.
$ bal build --cloud="aws_lambda"
Tip: You can append the
--graalvmflag to the above build command to build the native executable. This executable will have a much smaller memory footprint and faster startup time. For more information, see Build the executable in a container.
Info: To package the function as a container image instead of a ZIP file, see Deploy the function as a container image.
Deploy the function
The AWS Lambda functionality in Ballerina is implemented as a custom AWS Lambda layer. This information is provided when the function is created. The compiler generates the aws-ballerina-lambda-functions.zip file, which encapsulates all the AWS Lambda functions that are generated. This ZIP file can be used with the AWS web console or the AWS CLI to deploy the functions.
To deploy the function, execute the command, which you get in the CLI output logs after you build the function. For examples, see Examples.
Note: When you are deploying, make sure to replace the
$FUNCTION_NAME,$LAMBDA_ROLE_ARN, and$REGION_IDplaceholders with the corresponding values you obtained when setting up the AWS account and creating a role.
Info: The functions are deployed on the
provided.al2023custom runtime. Theprovidedruntime, which earlier versions used, has been retired by AWS and can no longer be used to create or update a function.
Info: For the supported parameters, go to the
create-functiondocumentation. You might need to change parameters such as theMemorySizeandTimeoutdepending on your application and connection speed.
Redeploy the function
After the function is created, you can push a new build of it by executing the aws lambda update-function-code command, which you also get in the CLI output logs after you build the function. The command uploads the regenerated ZIP file, or refers to the new image with the --image-uri parameter when the function is deployed as a container image. The runtime, the role, and the layer are configured when the function is created. Therefore, they are not repeated when you redeploy.
A function created before AWS retired the provided runtime in February 2024 is still configured with a runtime that can no longer be selected, and it cannot be redeployed as it is. Move it to provided.al2023 once by executing the command below, and then redeploy as usual.
$ aws lambda update-function-configuration --function-name $FUNCTION_NAME --runtime provided.al2023
Deploy the function as a container image
Building with --cloud="aws_lambda" produces a ZIP deployment package, which AWS Lambda limits to 250 MB unzipped. To package the function as a container image instead, which supports images of up to 10 GB, execute the command below.
$ bal build --cloud="aws_lambda_image"
The image is named after the package and tagged with the package version. To deploy it, execute the commands, which you get in the CLI output logs, to create an Amazon ECR repository, push the image to it, and create the function from the pushed image.
The handler is selected per function at deployment time with the --image-config parameter of the create-function command. Therefore, a single image can serve every @lambda:Function in the package.
Note: Install and configure Docker in your machine before building a container image.
Note: A package version that is not a valid Docker tag is converted to one, and the compiler reports the tag it used.
Tip: You can append the
--graalvmflag to the above build command to produce a native image on theprovided.al2023base image instead of a JVM image onpublic.ecr.aws/lambda/java:21, which gives a considerably smaller image and faster cold starts.
Route the invocation results to a destination
A function can route the result of an asynchronous invocation to another AWS service, without any code in the function itself, by declaring destinations in the annotation as shown below.
import ballerinax/aws.lambda; @lambda:Function { destinations: { onSuccess: "arn:aws:sqs:<REGION_ID>:<ACCOUNT_ID>:orders-processed", onFailure: "arn:aws:sns:<REGION_ID>:<ACCOUNT_ID>:alerts" } } public function processOrder(lambda:Context ctx, json input) returns json { return {status: "ok"}; }
Note: The destination ARNs are ordinary Ballerina strings. Therefore, replace the
<REGION_ID>and<ACCOUNT_ID>placeholders in the source itself before you build the package.
Either field can be omitted, and a destination can be an SQS queue, an SNS topic, an EventBridge event bus, or another Lambda function. Destinations are configured separately from the function itself. Therefore, the compiler prints an aws lambda put-function-event-invoke-config command alongside the deploy commands, which you execute after the function is deployed.
AWS delivers a record that describes the invocation to the destination rather than the value the function returned. A successful invocation delivers the request payload and the response payload with a condition of Success, as shown below. A failed one carries a condition of either RetriesExhausted, when the function kept failing until the retries ran out, or EventAgeExceeded, when the event waited longer than the MaximumEventAgeInSeconds configured for the function.
{ "version": "1.0", "requestContext": {"condition": "Success", "approximateInvokeCount": 1}, "requestPayload": {"hello": "world"}, "responsePayload": {"status": "ok"} }
Note: The
destinationsfield configures function-level destinations, which apply to asynchronous invocations only. A function invoked synchronously, such as through a function URL or through anaws lambda invokecommand without--invocation-type Event, returns its result to the caller instead. An event source mapping that polls a queue or a stream also invokes the function synchronously and does not use this field, although some event source mappings support anOnFailuredestination of their own, which is configured on the mapping rather than on the function.
Note: The execution role of the function needs permission to write to the destination (e.g.,
sqs:SendMessagefor an SQS queue). Without it, the invocation succeeds but the record is never delivered, and Lambda reports this through theDestinationDeliveryFailuresCloudWatch metric rather than as an invocation error.
Info: Every field of the annotation is optional. Therefore,
@lambda:Functionremains valid with no annotation value, and existing functions require no change.
CI/CD deployment with AWS Lambda
This Ballerina GitHub action workflow automates the continuous integration and continuous deployment (CI/CD) process for a Ballerina project. It is triggered by every push to the repository and automatically builds the project.
name: Ballerina CI/CD with AWS Lambda on: [push] jobs: build-and-deploy: runs-on: ubuntu-latest steps: # Step 1: Checkout the Repository - name: Checkout uses: actions/checkout@v4 # Step 2: Build the Ballerina Project - name: Ballerina Build uses: ballerina-platform/ballerina-action@master with: args: build --cloud=aws_lambda # Step 3: Configure AWS Credentials - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ secrets.AWS_REGION }} # Step 4: Deploy to AWS Lambda - name: Deploy to AWS Lambda run: | aws lambda update-function-code \ --function-name your_lambda_function_name \ --zip-file fileb://target/aws_lambda/aws-ballerina-lambda-functions.zip # Optional: Test the Lambda Function - name: Invoke Lambda Function run: | aws lambda invoke --function-name your_lambda_function_name --payload '{}' \ --cli-binary-format raw-in-base64-out response.json cat response.json
- Step 01: The push event can initiate a build and deployment process
- Step 02: This command tells Ballerina to compile the project specifically for AWS Lambda. It optimizes the output for Lambda’s serverless runtime requirements, and generates the
aws-ballerina-lambda-functions.zipdeployment package in thetarget/aws_lambdadirectory. - Step 03: Ensures that the workflow has the necessary credentials to interact with AWS services.
- Step 04: Deploy the packaged Ballerina function to AWS Lambda by uploading the generated ZIP file.
- Step 05: Test the deployed Lambda function by invoking it and outputting the response to confirm that the function works as expected.
Note: The workflow above updates a function that already exists. Create it once with the
aws lambda create-functioncommand, which you get in the CLI output logs after you build the function.
Examples
In a more practical scenario, the AWS Lambda functions will be used by associating them with an external event source such as Amazon DynamoDB or Amazon SQS. For more information on this, go to AWS Lambda event source mapping documentation.
For examples of the usage of AWS Lambda functions, see AWS Lambda.
Azure Functions
The Azure Functions extension provides the functionality to expose a Ballerina function as a serverless function in the Azure Functions platform.
Info: Azure Functions can be written in Ballerina using the listeners and services provided by the Azure Functions package.
Supported triggers and bindings
An Azure Function consists of a trigger and optional bindings. A trigger defines how a function is invoked. A binding is an approach in which you can declaratively connect other resources to the function.
There are input and output bindings. An input binding is a source of data that flows into the function. An output binding allows outputting data from the function to an external resource. For more information, go to Azure Functions triggers and bindings concepts.
The following Azure Functions triggers and bindings are currently supported in Ballerina.
| Supported triggers | Supported output bindings | Supported input bindings |
|---|---|---|
| HTTP | HTTP | - |
| Queue | Queue | - |
| Blob | Blob | Blob |
| - | Twilio SMS | - |
| Cosmos DB | Cosmos DB | Cosmos DB |
| Timer | - | - |
Follow the instructions in the sections below to deploy a Ballerina function in Azure Functions.
Set up Azure Functions
Follow the steps below to set up the Azure Functions.
- Create an Azure subscription.
- Install the Azure CLI.
- Install and configure the Functions Core Tools.
Create the app
Follow the steps below to create the Azure function app.
Note: Make sure to remember the function application name and storage account name as they will be required in the code samples.
-
Execute the
az logincommand on the CLI to log in to the Azure CLI. -
Create an Azure Function app with the default option of creating a resource group automatically and the requirements below.
- Runtime stack -
Java 21 - Hosting operating system -
Windows(Currently, Linux is not supported in Azure by default for custom handlers.)
- Runtime stack -
Create the function
You can write Azure functions that use different triggers to invoke the function, and output bindings to generate the response based on your use case.
Info: For examples of creating Azure functions, see Examples.
Build the function
The Azure Functions functionality is implemented as a compiler extension. Therefore, artifact generation happens automatically when you build a Ballerina module by executing the command below.
$ bal build --cloud="azure_functions"
Deploy the function
To deploy the function, execute the command, which you get in the CLI output logs after you build the function. For examples, see Examples.
Note: When you are deploying, make sure to replace the
<function_app_name>placeholder with the app name of the created function.
Azure Functions as a native executable
You can use Azure Functions in the native approach of Ballerina.
Set up the prerequisites
Follow the steps below to set up the prerequisites, which are specifically required to follow the native approach.
- Set up the general prerequisites.
- Install and configure Docker in your machine.
- Install and configure GraalVM in your machine.
Create the app
Create an Azure Function app with the given resource group with the following requirements.
- Runtime stack -
Java 21 - Hosting operating system -
Linux(As of now, Ballerina Azure functions native support is only available for Linux.)
Create the function
An Azure Functions package supports the two build options below.
Info: Both build options for the non-native JVM-based approach will behave the same since the JVM is platform-independent.
cloud="azure_functions"- Builds the native executable to be compatible with the Azure Functions cloud.cloud="azure_functions_local"- Builds the native executable to be compatible with your machine for development purposes.
You can use these build options along with the graalvm build option (--graalvm) while building the package, as shown below.
Build locally
The example command below will build the package and run it locally.
$ bal build --cloud="azure_functions_local" --graalvm
Info: This will use the GraalVM you installed on your machine to build the native image and make the generated executable compatible with your machine.
Build for the cloud
The example command below will build the package for the Azure Functions cloud.
$ bal build --cloud="azure_functions" --graalvm
Info: This will perform the compilation inside the Docker image to make it compatible with the Azure Functions cloud environment.
Deploy the function
You can either deploy the Azure Functions package locally or on the Azure Functions cloud.
Deploy locally
The example command below will deploy the package locally.
$ func start --script-root target/azure_functions
Deploy on the cloud
The example command below will deploy the package on the Azure Functions cloud.
$ func azure functionapp publish <function_app_name> --script-root target/azure_functions
CI/CD with Azure Functions
This GitHub Action workflow automates the continuous integration and continuous deployment (CI/CD) process for a Ballerina project. It is triggered by every push to the repository and automatically builds the project.
name: Ballerina CI/CD with Azure Functions on: [push] jobs: build-and-deploy: runs-on: ubuntu-latest steps: # Step 1: Checkout the Repository - name: Checkout uses: actions/checkout@v2 # Step 2: Build the Ballerina Application - name: Build Ballerina Application run: bal build # Step 3: Set up Azure Functions Core Tools - name: Set up Azure Functions Core Tools uses: philippelemaire/install-azure-functions-core-tools@v1 # Step 4: Deploy to Azure Functions - name: Azure Functions Action uses: Azure/functions-action@v1 with: app-name: 'your-function-app-name' package: './' env: AZURE_FUNCTIONAPP_PUBLISH_PROFILE: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }} # Optional: Test the Azure Function - name: Invoke Azure Function run: | curl https://your-function-app-name.azurewebsites.net/api/your-function-name
- Step 01: The push event can initiate a build and deployment process
- Step 02: This step compiles the Ballerina application to generate an executable JAR file. The
bal buildcommand compiles the code and packages it, making it ready for deployment. - Step 03: Install the Azure Functions Core Tools, which are necessary for deploying and testing Azure Functions in this GitHub Actions environment.
- Step 04: Deploy the built Ballerina application to an Azure Function App. This step uploads the packaged function to Azure and configures it for serverless execution.
Examples
For examples of the usage of Azure Functions, see Azure Functions.