visit
Listing 1: Ballerina HTTP CRUD Service Template
The above service is a Ballerina service template for a CRUD data service. In the service, we can define individual resource functions. The resource functions mentioned here are getEmployee, getEmployeeById, addEmployee, updateEmployee and deleteEmployee. The operations these resources represent are self-explanatory and they are each represented by its corresponding HTTP verb, the resource paths, and the payloads it contains.
Ballerina services have data binding support in the service resources, where its path parameters and payload can be directly mapped to the parameters in the resource. A path parameter example is shown in the employeeById resource function, where its id section in the path is mapped to the id integer value in the resource function.
Also, in the employeeInsert resource function, its body payload is mapped directly to the Employee record type. In this case, the incoming JSON is mapped to the fields of the Employee record. If needed, for any custom mappings, we can also declare the raw JSON object to be retrieved as the parameter to the resource as well. (For more information on Ballerina services, and their configuration properties, refer to the “HTTP/HTTPS” section of ).
jdbc:Client empDB = check new ("jdbc:mysql://localhost:3306/EmpDB", "root", "root");
Listing 2: Ballerina SQL Database Client Endpoint Declaration
The client endpoint is used in Ballerina by invoking the remote methods in them. A special syntax, which is an arrow notation (->), is used to access the remote methods in a client endpoint. This symbolizes the operation of doing a network call using the client.
Listing 3: Reading Records with Database Client
The code segment above shows how the database endpoint can be used to execute an SQL query with the given record type (Employee) and the arguments (id) for the query. This returns a stream with a record value type (for more information on using the database connectors, refer to the “Database” section of).
After this, the caller endpoint can be used to respond to the client with the payload. This is something unique to Ballerina. Other frameworks such as JAX-RS/WS simply return a function and if there is a problem in writing the result back to the client, we do not have a place to handle it properly. In Ballerina, we get that chance to do this, since we are sending the response back explicitly in the code.
The SQL query shown in Listing 3 provides a parameterized query to the query remote method invocation. The parameterized query is based on in Ballerina. In this way, we can naturally provide the values of parameters in the query string itself. The query remote method accepts both parameterized queries and simple string values.
Data updates and insertions are done with the execute remote method in the JDBC client. Listing 4 shows how this is done with the operation to add new employees to the database table.
Listing 4: Writing Records with Database Client
When inserting multiple records to a database table, we can get a significant performance boost by batching multiple records at once and doing batch inserts to the database. This is possible with the use of the batchExecute remote method. Let’s add a new resource function named addEmployeeBatch to demonstrate this functionality.
Listing 5: Writing Batch Records with Database Client
As seen in Listing 5, the batch request is executed by providing an array of sql:ParameterizedQuery objects, which is generated by using raw templates in conjunction with the features of Ballerina.
Listing 6: Invalid SQL Query Usage with Arguments
The code stated in Listing 6 has a clear SQL injection attack vulnerability. So in typical programming languages, the developer must catch this and fix it properly. In Ballerina, the above code doesn’t even compile! The reason being the in-built taint analysis features available in the language. Here, the SQL query string parameter is marked as “untainted”. So unless the variable id is explicitly “untainted”, the SQL query string derived from the concatenation is marked as “tainted”, thus making it not compatible with the untainted value expected as the SQL query string for the query operation.
This shows that Ballerina does its best to stop developers from making any mistakes from the get-go. This is the concept of security by default followed throughout Ballerina.
Listing 7: Service with Authentication Configuration
The service definition above contains an authentication configuration along with a transport level secure endpoint, where the keystore information is given. In this configuration, the service is declaring that only users with the scope “employee-data-access” are allowed to call this service.For more information on Ballerina authentication framework features, please read .Listing 8: Ballerina Transactions with the SQL Connector
Listing 8 shows a transaction block in action. There, the operations represent a scenario where the employee team is swapped between two employees. For this, we first read in the two employee records, swap the record data, and perform separate SQL update operations to do the swap. All these operations need to be done in a single transaction to make sure we don’t get into an inconsistent state. So here, what we need to simply do is, wrap all the SQL connector operations in a transaction block and do either a commit or rollback action accordingly.
Figure 1: Ballerina Service Metrics
Figure 2: Ballerina SQL Metrics
For more information on Ballerina observability features, check out this article on automated observability
curl -X POST -d "{'id':1, 'name':'Jordyn Bird','age':55, 'team': 'Sales'}" //localhost:8080/data/employees
curl -X POST -d "{'id':2, 'name':'Emily Smith','age':45, 'team': 'Marketing'}" //localhost:8080/data/employees
curl -X POST -d "[{'id':3, 'name':'John Doe','age':25, 'team': 'Engineering'}, {'id':4, 'name':'Jane Doe','age':25, 'team': 'Engineering'}]" //localhost:8080/data/employees_batch
curl -X GET //localhost:8080/data/employees
[{"id":1, "name":"Jordyn Bird", "age":55, "team":"Sales"}, {"id":2, "name":"Emily Smith", "age":45, "team":"Marketing"}, {"id":3, "name":"John Doe", "age":25, "team":"Engineering"}, {"id":4, "name":"Jane Doe", "age":25, "team":"Engineering"}]
curl -X GET //localhost:8080/data/employees/2
{"value":{"id":2, "name":"Emily Smith", "age":45, "team":"Marketing"}}
curl -X PUT -d "{'id': 1, 'name':'Sunil Perera','age':66, 'team': 'Sales'}" //localhost:8080/data/employees
curl -X GET //localhost:8080/data/employees
[{"id":1, "name":"Sunil Perera", "age":66, "team":"Sales"}, {"id":2, "name":"Emily Smith", "age":45, "team":"Marketing"}, {"id":3, "name":"John Doe", "age":25, "team":"Engineering"}, {"id":4, "name":"Jane Doe", "age":25, "team":"Engineering"}]
curl -X POST //localhost:8080/data/employee_team_swap/1/2
curl -X GET //localhost:8080/data/employees
[{"id":1, "name":"Sunil Perera", "age":66, "team":"Marketing"}, {"id":2, "name":"Emily Smith", "age":45, "team":"Sales"}, {"id":3, "name":"John Doe", "age":25, "team":"Engineering"}, {"id":4, "name":"Jane Doe", "age":25, "team":"Engineering"}]
curl -X DELETE //localhost:8080/data/employees/1
curl -X GET //localhost:8080/data/employees
[{"id":2, "name":"Emily Smith", "age":45, "team":"Sales"}, {"id":3, "name":"John Doe", "age":25, "team":"Engineering"}, {"id":4, "name":"Jane Doe", "age":25, "team":"Engineering"}]