How to write Azure Functions in Scala - Basic Example
Introduction
Azure Functions are an easy and quick way of deploying code for specific functions like processing data or attending to HTTP requests.
As Azure Functions support Java, we can easily use that SDK to write our functions in Scala, in case that you need it.
Configuring the Scala Project
Create a new Scala project. I normally use maven with this archetype: net.alchim31.maven: scala-archetype-simple: 1.7 and then remove from the pom everything I don’t need.
Maven configuration
In this case, for writing an Azure Function, you need to have the following code in your pom:
- Properties:
Scala version: here I'm using scala 2.12.12.
Java version: 1.8 in this case, but versions like 17 are also supported.
I'm also using these properties section to define variables needed later for the deployment of the function to azure:
Azure Functions java library version: Check the latest version available here: Maven Repository: com.microsoft.azure.functions » azure-functions-java-library (mvnrepository.com)
Function App Name: Name of your function in azure.
Resource Group Name: Name of the resource group where you’re going to put the function.
App Service Plan: The name of the plan associated with the function.
Azure Region: Region in azure where your resources are deployed.
<properties>
<scala.version>2.12.12</scala.version>
<java.version>1.8</java.version>
<azure.functions.maven.plugin.version>1.22.0</azure.functions.maven.plugin.version>
<azure.functions.java.library.version>2.2.0</azure.functions.java.library.version>
<azure.functionAppName>function-name</azure.functionAppName>
<azure.resourceGroup>azure-resource-group-name</azure.resourceGroup>
<azure.appServicePlanName>azure-app-service-plan</azure.appServicePlanName>
<azure.region>azure-region</azure.region>
</properties>
- Dependencies: add the azure functions library.
<dependencies>
.....
<dependency>
<groupId>com.microsoft.azure.functions</groupId>
<artifactId>azure-functions-java-library</artifactId>
<version>${azure.functions.java.library.version}</version>
</dependency>
- Build: plugin configuration - In the build section, add your function configuration.
<build>
<plugins>
...
<plugin>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-functions-maven-plugin</artifactId>
<version>${azure.functions.maven.plugin.version}</version>
<configuration>
<!-- function app name -->
<appName>${azure.functionAppName}</appName>
<!-- function app resource group -->
<resourceGroup>${azure.resourceGroup}</resourceGroup>
<!-- function app service plan name -->
<appServicePlanName>${azure.appServicePlanName}</appServicePlanName>
<!-- function app region-->
<!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-regions for all valid values -->
<region>${azure.region}</region>
<runtime>
<!-- runtime os, could be windows, linux or docker-->
<os>linux</os>
<javaVersion>8</javaVersion>
</runtime>
<appSettings>
<property>
<name>FUNCTIONS_EXTENSION_VERSION</name>
<value>~4</value>
</property>
</appSettings>
</configuration>
<executions>
<execution>
<id>package-functions</id>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Other files needed
You also need to add the following files to the root of your project:
- host.json
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[3.*, 4.0.0)"
}
}
- local.settings.json: for running the function locally.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "java"
}
}
Writing the function’s code
Create a new class. Source file
package wiki.dataengineering
import com.microsoft.azure.functions.{ExecutionContext, HttpMethod, HttpRequestMessage, HttpResponseMessage, HttpStatus}
import com.microsoft.azure.functions.annotation.{AuthorizationLevel, FunctionName, HttpTrigger}
import java.util.Optional
class TestFunction {
@FunctionName("Test")
def run(@HttpTrigger (name = "req", methods = Array(HttpMethod.POST, HttpMethod.GET), authLevel = AuthorizationLevel.ANONYMOUS) request: HttpRequestMessage[Optional[String]], context: ExecutionContext): HttpResponseMessage = {
context.getLogger.info("Function executed")
context.getLogger.info(request.getHttpMethod.toString)
context.getLogger.info(request.getUri.toString)
context.getLogger.info(request.getBody.toString)
request.createResponseBuilder(HttpStatus.OK).body("It Works!").build()
}
}
In order to write your own, what you need to do is to create a function and add the @FunctionName("") decorator.
That run method can have any of the existing triggers available for azure Functions. (https://learn.microsoft.com/en-us/azure/azure-functions/functions-triggers-bindings?tabs=java#bindings-code-examples)
In this case, we're using an HTTP trigger that accepts POST and GET methods, and it can be accessed without authentication.
We have also specified that the input body will be a String.
Something really nice about this, It is that you can specify a scala class as your input, and the framework will automatically try to serialize the JSON body of the request (if there is one), to that class.
case class InputMessage (field1: String, field2: Int)
...
def run(@HttpTrigger (.... request: HttpRequestMessage[Optional[InputMessage]], context: ExecutionContext): HttpResponseMessage = {
With that “InputMessage” case class definition, the function will expect a body like this:
{
"field1": "test",
"field2": 1
}
Running the function locally
From a terminal or from Intellij’s integrated maven type:
mvn clean package
This will recompile the code and generate an artifact (a jar file) with the code.
If you want to run the function locally, run after the previous command the following:
mvn azure-functions:run
If it’s the first time you’re running it, it may give you the following error:
To fix it, simply follow that link and install the Azure Functions Core tools. If you’re using Intellij, you may need to close and open it again before you can use it.
Then you should be able to see the logs of your function:
You can use that URL to check if the function works as expected:
- GET Request:
- POST Request:
Defining multiple functions
In order to define multiple functions, you only need to write more classes following the same pattern, or add more methods with the FunctionName decorator:
They will all spin up when you deploy the code.
Deployment on Azure
If you have everything setup in the pom, as we indicated at the beginning of the post, deploying the azure function is as simply as running the following command.
mvn azure-functions:deploy
Code
You can find the code of this project here: https://github.com/data-engineering-content/azure-functions-scala