From time to time we get asked if Oracle REST Data Services ( ORDS ) can support certain formats or specifications such as YAML, OData or JSON-RPC. The simple answer is yes. Previously I provided an example plugin to convert the JSON response from ORDS into a YAML structure. A plugin is not the only option. One can create a user defined resource to implement almost anything. If using Oracle 23ai, that implementation can be in PL/SQL or JavaScript.
So let’s look at how one can use JSON-RPC with ORDS and Oracle 23ai in the hosted Oracle Autonomous Database.
JSON-RPC Protocol
The JSON-RPC protocol offers a minimal, transport-agnostic mechanism for structured remote procedure calls using JSON. It’s widely used in blockchain, embedded systems, and distributed APIs.
With Oracle Database 23ai, you can now implement a JSON-RPC endpoint directly inside the database using ORDS and the Multilingual Engine (MLE) with JavaScript — no extra middle tier or external Node.js runtime required.
In this post, we’ll build a standards-compliant JSON-RPC handler using ORDS MLE JavaScript, based on the canonical example from Wikipedia.
The example implementation will just perform subtract, no other methods. However, this is essentially what the documented JSON-RPC V2 example is all about.
Using ORDS and Oracle 23ai with Autonomous Database
The entire example brings together a number of components and hosted services. One can accomplish all this on-premise, but it is more effort. Let’s briefly talk about those key constituents…
Oracle Autonomous Database (Serverless) is a fully managed, cloud-native database service on Oracle Cloud Infrastructure (OCI) that eliminates the complexity of database administration. It automatically handles provisioning, scaling, patching, tuning, backups, and security. The serverless deployment model allows developers to focus purely on data and logic without worrying about infrastructure or resource management.
Oracle Database 23ai introduces advanced AI and developer features, including the Multilingual Engine (MLE) — a lightweight JavaScript runtime embedded inside the database. MLE allows developers to write and run JavaScript code natively alongside SQL and PL/SQL, enabling modern use cases like JSON processing, protocol translation, and procedural logic without leaving the database.
Oracle REST Data Services (ORDS) is also hosted and managed within the Autonomous Database environment. ORDS enables developers to expose RESTful APIs directly from SQL, PL/SQL, or JavaScript (via MLE) using the Oracle 23ai database. It supports secure endpoints, OAuth2, pagination, OpenAPI integration, and more — without the need for an external application server.
Together, this stack allows developers to:
Build microservices and APIs with JavaScript inside the database
Expose data securely using REST over HTTPS via hosted ORDS
Deploy completely serverless solutions on Oracle Cloud, fully managed and auto-scaled
Many of these are accessible for free in the OCI Free Tier. Yes for free.
Step by step
In this example I’ll start with an Oracle 23ai database in the Frankfurt region using the Free Tier. I have a REST Enabled database user called JSONRPCEXAMPLE with the standard roles to create a database session and resources in the database.
Granted Roles for JSONRPCEXAMPLE database user
🔒 Step 1: Grant Required Privileges
Using Oracle 23ai is a necessity because it contains the Multi Language Engine for running JavaScript but each database user does require permission to use it. My JSONRPCEXAMPLE database user has been given that permission:
GRANT EXECUTE ON JAVASCRIPT TO JSONRPCEXAMPLE; GRANT EXECUTE DYNAMIC MLE TO JSONRPCEXAMPLE;
Granting required MLE privileges.
In my hosted Oracle 23ai database I executed the above as the ADMIN user.
Now to connect as the REST Enabled JSONRPCEXAMPLE user and actually implement that User Defined Resource.
🚀 Step 2: Create the ORDS Module and Handler
There are plenty of options to get a connection to the database for running this SQL Script. The most popular options are sqlcl, SQL Developer for VS Code and Database Actions.
Use the following script to define the ORDS module, template, and MLE handler. This version is fully correct for Oracle 23ai and adheres to the format specified in the ORDS Developer Guide.
Since this is only an implementation for subtract, you can take it on as an additional exercise to introduce more calculator operations.
Also consider moving the logic into an MLE Module or even using a predefined JavaScript Mathematics library for your calculator logic. See Transform your SQL Results with Mustache and ORDS JavaScript Handlers for an example of using JavaScript libraries through the MLE Module concept.
You have probably noticed that the handler always returns HTTP Status Code 200 even if there is an error. The JSON-RPC Error Object section of the specification does not state any expected behaviour around the transport protocol for an error scenario so you will have to determine what is appropriate for your organisation.
Try it without specifying an id or without params to see how the implementation responds.
You will also note that this service is not secured and it would be best practice to restrict access to authorised users only. This can be achieved by defining an ORDS Privilege for the module or pattern of the service URI. Take a look at Configuring Secure Access to RESTful Services to find out more.
✅ Conclusion
Oracle Database 23ai’s MLE JavaScript support allows you to run lightweight protocol handlers—like JSON-RPC—directly in the database, without the need for a plugins. With just a few lines of code, ORDS becomes a smart, standards-compliant backend for modern APIs to meet your integration needs.
Key takeaways:
Use (req, resp) => {} function syntax for MLE JavaScript in ORDS.
Grant EXECUTE ON JAVASCRIPT and EXECUTE DYNAMIC MLE to your user.
Validate incoming JSON-RPC payloads according to spec.
In a previous post, we explored how to use the Mustache template engine with ORDS mle/javascript Handlers to transform SQL query responses into application/json responses. If you missed that, check it out – Transform your SQL Results with Mustache and ORDS JavaScript Handlers.
The idea was inspired by a tweet from @FriedholdMatz and I put together an initial implementation at the time which I have since refined.
Easily done with @OracleREST ! I haven't written this up yet…TEMPLATES table contains a list of templates and mle/javascript handler does the work 👍 pic.twitter.com/mRQvTcbQJE
This follow-up dives deeper into the concept of using Mustache as a service, enabling users to submit their own template definitions and payloads for dynamic transformations. This service can be incredibly useful when you need to allow external users or applications to define how they want data structured without modifying your code every time.
In fact, this article is going to cover a wide range of subjects related to developing services with ORDS. These include:
Use of implicit parameters both in query, PL/SQL and mle/javascript handlers.
Protecting all services defined in a module using a single privilege definition.
Invoking the secured services from Postman using OAuth 2.0 Client Credentials to automatically obtain a new bearer token.
Let’s walk through the implementation steps. As a REST Enabled user connect to your 23ai database where you have already defined your MLE library for Mustache as covered in the previous article. Just like in that article, our REST Enabled user in this article is the HR schema and all statements are executed in the database as that user.
NOTE! The following steps are based on Oracle 23ai MLE database objects which you should have created from Transform your SQL Results with Mustache and ORDS JavaScript Handlers. Having the MLE Module for Mustache and an MLE library making it available to mle/javascript handlers is a prerequisite.
Step 1: Setting up the Database Table for Mustache Templates
First, we need a database table to store the template definitions. This table will allow clients to create and manage their templates, identified by a unique ID.
CREATE TABLE mustache_templates (
template_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
template_name VARCHAR2(100) not null,
template_text CLOB not null,
template_owner VARCHAR2(200) not null,
template_content_type VARCHAR2(100) not null,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
In this table, template_name is used for a meaningful description of the Mustache template, while template_text will store the Mustache template itself. You will also notice a template_owner column which we will use to keep the rows distinct for each template owner. Put simply, a user, identified by their OAuth client identifier, manages their own templates. Both the primary key template_id and the created_at columns are automatically assigned values on insert.
Step 2: Defining ORDS services for Mustache Template creation, retrieval and use
Now, we need an API endpoint to submit, retrieve and use templates. Access to each row is going to be restricted based on the OAuth client identifier so we will require a slightly more complex set of logic than what REST Enabling the mustache_templates table will provide. That will all be taken care of in one module with base path = /templates/ and a few of REST Service templates defined. These are:
/templates/. – This will have a GET handler to retrieve all rows the user has access to and a POST handler to add new Mustache templates.
/templates/:id – This will have a GET handler to retrieve a specific Mustache template by template_id and a PUT handler to update that specific Mustache template.
/templates/id/generate – This will have a single POST handler to apply the Mustache template to the request body and return the generated content.
The GET handlers will be simple query handlers. The PUT and handler will involve a PL/SQL block. One of the POST handlers will be defined using PL/SQL and the other will use JavaScript. In all cases they will use the ORDS provided implicit parameter for referencing the current user which will be the Client ID of the OAuth client used at runtime.
Define the ORDS REST module
This PL/SQL block will create the module which all the service handlers will belong to.
ORDS uses templated REST Service definitions and we will create three now:
BEGIN
ORDS.DEFINE_TEMPLATE(
p_module_name => 'templates',
p_pattern => '.',
p_comments => 'Retrieve all Mustache templates the runtime user has access to and create new ones');
ORDS.DEFINE_TEMPLATE(
p_module_name => 'templates',
p_pattern => ':id',
p_comments => 'Retrieve or update a specific Mustache template');
ORDS.DEFINE_TEMPLATE(
p_module_name => 'templates',
p_pattern => ':id/generate',
p_comments => 'Transform the request payload using the specified Mustache template');
COMMIT;
END;
With these in place the next step is to define the relevant handlers for the GET, PUT and POST methods.
GET: /templates/
This will retrieve all Mustache template records that the runtime user has access to. This query uses the implicit parameter :current_user and a special column alias $.id which is used to generate a self reference link in the response.
BEGIN
ORDS.DEFINE_HANDLER(
p_module_name => 'templates',
p_pattern => '.',
p_method => 'GET',
p_source_type => 'json/collection',
p_source =>
'select TEMPLATE_ID as "$.id",
TEMPLATE_ID, TEMPLATE_NAME, TEMPLATE_CONTENT_TYPE, TEMPLATE_TEXT, CREATED_AT from MUSTACHE_TEMPLATES where TEMPLATE_OWNER = :current_user');
COMMIT;
END;
The above handler will return a response similar to this when invoked by an authorised user:
This handler is a PL/SQL handler which will insert a new Mustache template record. This handler has some content of note.
The implicit parameter :current_user is used to limit queries to rows that the runtime user has access to.
Once the runtime user has 10 Mustache templates they can not create any more.
The implicit parameters :status_code and :forward_location are used to generate a reference to the just inserted Mustache template. ORDS will generate a response body containing the json representation of that new record.
You will notice that parameters are not defined for :template_name, :template_content_type and :template_text. When not explicitly defined they are assumed to be fields in the request body.
The p_mimes_allowed argument makes it clear that application/json payloads are expected by this handler.
BEGIN
ORDS.DEFINE_HANDLER(
p_module_name => 'templates',
p_pattern => '.',
p_method => 'POST',
p_source_type => 'plsql/block',
p_mimes_allowed => 'application/json',
p_source =>
'DECLARE
existing_templates_count NUMBER;
new_template_id NUMBER;
BEGIN
select count(*) into existing_templates_count from mustache_templates where TEMPLATE_OWNER = :current_user;
if existing_templates_count < 10 then
insert into MUSTACHE_TEMPLATES (TEMPLATE_NAME, TEMPLATE_CONTENT_TYPE, TEMPLATE_TEXT, TEMPLATE_OWNER) VALUES(:template_name, :template_content_type, :template_text, :current_user)
RETURNING template_id INTO new_template_id;
:status_code := 201;
:forward_location := new_template_id;
else
-- Too many records
:status_code := 400;
end if;
END;');
COMMIT;
END;
This handler will take a request payload such as the following…
This will retrieve a Mustache template by its unique identifier. Similar to the GET /templates/ handler it is a simple query but uses the :current_user implicit parameter and the $.id alias for generating a self link in the response. Note that the value for :id in the query comes from the URL path pattern of the template.
BEGIN
ORDS.DEFINE_HANDLER(
p_module_name => 'templates',
p_pattern => ':id',
p_method => 'GET',
p_source_type => 'json/item',
p_source =>
'select
TEMPLATE_ID as "$.id",
TEMPLATE_ID,
TEMPLATE_NAME,
TEMPLATE_CONTENT_TYPE,
TEMPLATE_TEXT,
CREATED_AT
from MUSTACHE_TEMPLATES
where TEMPLATE_OWNER = :current_user and TEMPLATE_ID = :id');
COMMIT;
END;
This returns the specific Mustach template requested so one can see what it produces based on the content in the template_text. Hopefully the template_name is descriptive too 😀
PUT: /templates/{id}
This handler will update an existing Mustache template if the runtime user has access to the row. Similar to the POST /templates/ handler it uses implicit parameters for the SQL query but also to instruct ORDS on how to generate the response.
And now the mle/javascript handler that will apply the Mustache template to the request body to return a generated document. The request payload will be the data to be used into the Mustache template which is specified by the templates identifier. This endpoint will:
Retrieve the template corresponding to template_id.
Use the JavaScript Mustache engine to transform the payload.
Similar to the mle/javascript handler in the previous article it relies on an MLE environment to reference the Mustache template. Other items to note about this mle/javascript handler:
Checks the content_type implicit parameter and returns an appropriate status code if it is not application/json.
Provides fetchInfo to database session so that it knows how to represent CLOB values from the TEMPLATE_TEXT column.
Refers to the :id parameter using the uri_parameters of the request.
Invoking this service specifying the Mustache template id in the URL will transform the request payload into a generated document using the specified template. It will even set the response content type as specified by the Mustache template.
The eagle eyed reader will have noticed that this mle/javascript handler source is checking that the content type of the request is application/json and strictly speaking the Mustache Templating Engine can work with more than JSON as the source context. As an additional homework exercise you could explore working with different request payload structures.
Step 3: Protecting the Services
For this service we’re going to restrict access to only authenticated users. Note that every handler refers to the implicit parameter :current_user. Every row will belong to a specific template_owner user and that will correspond to an OAuth client that we can issue for each prospective user. To achieve that a Role must be defined and a Privilege explicitly protecting the module created earlier is required. Run this…
DECLARE
L_PRIV_ROLES owa.vc_arr;
L_PRIV_PATTERNS owa.vc_arr;
L_PRIV_MODULES owa.vc_arr;
BEGIN
ORDS.CREATE_ROLE(
P_ROLE_NAME => 'blog.peterobrien.MustacheTemplateUser'
);
L_PRIV_MODULES( 1 ) := 'templates';
L_PRIV_ROLES( 1 ) := 'blog.peterobrien.MustacheTemplateUser';
ORDS.DEFINE_PRIVILEGE(
P_PRIVILEGE_NAME => 'blog.peterobrien.MustachTemplate',
P_ROLES => L_PRIV_ROLES,
P_PATTERNS => L_PRIV_PATTERNS,
P_MODULES => L_PRIV_MODULES,
P_LABEL => 'Mustache Template Privilege',
P_DESCRIPTION => 'Protects access to the Mustache Template module',
P_COMMENTS=> 'Mustache Template module provides the managed and use of Mustache Template as a Service.'
);
COMMIT;
END;
Send a request to one of the handlers now and one should get a HTTP 401 response.
With the module now directly protected let’s create an OAuth client that can access it. Note that this is using OAUTH package but ORDS is moving to a new OAUTH_SECRETS package which I will refer to here when the documentation for it is published…
BEGIN
ORDS_METADATA.OAUTH.CREATE_CLIENT(
P_NAME => 'TemplateClient1',
P_GRANT_TYPE => 'client_credentials',
P_OWNER => 'HR',
P_DESCRIPTION => 'A client for the Mustache Template as a Service module',
P_SUPPORT_EMAIL => 'test@example.com',
P_PRIVILEGE_NAMES => 'blog.peterobrien.MustachTemplate'
);
ORDS_METADATA.OAUTH.GRANT_CLIENT_ROLE(
P_CLIENT_NAME => 'TemplateClient1',
P_ROLE_NAME => 'blog.peterobrien.MustacheTemplateUser'
);
COMMIT;
END;
Execute a select statement on USER_ORDS_CLIENTS to get the CLIENT_ID and CLIENT_SECRET for this TemplateClient1.
select
client_id, client_secret
from user_ords_clients
where name = 'TemplateClient1';
With that client_id and client_secret you can get an access token which can then be used to invoke the Mustache template services we have created. The :current_user value in our handlers will be the client_id value from the request.
The access token will expire after an hour and another one will have to be requested. Rather than going through all that manually we can use our REST client to manage that, even automatically requesting a new access token when the current one expires. In this article we’ll use Postman but there are alternatives. In fact if you are coding a client application to use the Mustache Template as a Service endpoints then a similar framework for handling the OAuth token lifecycle will be required. For Postman, we’ll define our authorisation flow and requests in a collection called Mustache.
In “My Workspace” I have a collection called Mustache
The collection is useful for keeping important metadata in one place such as how authentication and authorisation will be achieved…
In the Authorization definition of my Mustach collection I specify that I will use OAuth 2.0 as the Auth Type
Keep on scrolling down to enter specifics.
Let’s look at some of those specifics…
The token name doesn’t really matter. You can use any name you want.
The grant type should match your ORDS OAuth client definition: Client Credentials
The access token URL will be the full URL for your REST Enabled schema’s access endpoint. Generally that is <server>/ords/<schema alias>/oauth/token For example: https://ords.example.com/ords/hr/oauth/token
The client ID and client secret comes from the query on user_ords_clients earlier.
The client authentication should be Send as Basic Auth header. Postman will send the Client ID and Client Secret as basic authentication to the Access Token URL endpoint to get an access token.
Keep on scrolling down and press that Get New Access Token button to verify it all works.
With that in place then every request defined in the Mustache collection can just inherit that definition from the collection and the OAuth client token lifecycle is all automagically taken care of.
Of course, I’m running all this on my Oracle Free Tier 23ai Autonomous Database in Frankfurt
Note that if you import that Mustache Collection into Postman the hostname referred to will be ords-23ai.adb.eu-frankfurt-1.oraclecloudapps.com which doesn’t exist. You will have to change those entries to point to your own system.
Don’t want to set this up yourself but still want to try it out? Leave a comment below and I’ll get back to you with your own Client ID and Client Secret for you to use on a trial basis for a few days. All I have to do is create a new OAuth client with the same privilege and role and then forward you the Client ID and Client Secret. After a few days I can just delete the OAuth client.
Conclusion
By extending ORDS with Mustache templates as a service, you now have a flexible system that allows users to dynamically define and utilize templates. This can be a practical, reusable tool for building customisable reporting, notifications, or any scenario where the structure of data needs to be adaptable.
Feel free to experiment with more complex templates and explore how Mustache’s logic-less approach can simplify many data transformation tasks in your applications.
Stay tuned for future articles where we dive deeper into templating and ORDS functionalities!
If you have any questions or feedback, drop a comment below.
When dealing with SQL data, format and structure is crucial. Raw data isn’t always the most readable or useful format, especially when you need to expose it through an API. This is where Oracle REST Data Services (ORDS) comes in handy. It provides a secure and convenient JSON representation of the SQL query results in a JSON format by default. In this article, we’ll walk through using ORDS JavaScript handlers with the Mustache template engine to format an XML response from SQL query results. What we cover here is based on the previous article Multilingual Engine (MLE) for JavaScript Handlers. That article introduced an ORDS Module called demo_mle_javascript and in this article we will add to it.
The high level flow
Introducing the key components
Oracle REST Data Services (ORDS)
Oracle REST Data Services (ORDS) is a powerful tool that allows you to expose database functionalities as RESTful services. It simplifies the process of creating APIs for your Oracle database, making it easier to integrate with other systems and applications.
Mustache Template Engine
Mustache is a logic-less template engine that helps you format data into any desired format. Its simplicity and flexibility make it a great choice for rendering text in a wide variety of formats: HTML, JSON, XML, etc. Mustache uses templates to define the structure of the output, which can then be populated with data.
Oracle 23ai Database and the Multilangual Engine
With Oracle Database Multilingual Engine (MLE), developers have the option to run JavaScript code through dynamic execution or with persistent MLE modules stored directly in the database.
Bringing all together
Just as with the previous article which showcased Multilingual Engine (MLE) for JavaScript Handlers in ORDS we will use the 23ai database available at Oracle Autonomous Database. There we have a /ords/hr/demojs/employee/:id GET handler which has a JavaScript implementation for running a SELECT query to get an employee record for a employee_id provided in the URI.
In this article we’ll implement in JavaScript a GET handler to return all employee records but in an XML format. It will be very similar to the previous handler but reuse a JavaScript library so there’s actually less code. The additional step is that the JavaScript Mustache template engine will be imported into an MLE Module and then referred to by our JavaScript source in our ORDS JavaScript handler to transform the JSON representation of a query result set into an XML document returned by the service.
Defining the Mustache MLE Module
Mustache JavaScript
To use Mustache in ORDS JavaScript handler, you need to define it as an MLE module so that it can be imported by the dynamic MLE JavaScript in the handler source. The simplest way to get this done is include it from a content delivery network like jsDelivr. Here’s the URL for Mustache version 4.2.0: https://cdn.jsdelivr.net/npm/mustache@4.2.0/mustache.mjs.
The availability of JavaScript components like this supports self-contained and reusable code, key to developing successful software projects. We will use this library unmodified. However, due to a difference in architecture, module imports behave slightly differently in the Oracle Database when compared to other development environments. For example, JavaScript source code used with Node.js is stored in a specific directory structure on disk. MLE modules are stored together with the database, rather than in a file system, so must be referenced in a different manner. Let’s get that Mustache MLE module defined
Creating the MLE Module in Autonomous Database
The simplest way to define a module, from an external source, in an 23ai Autonomous Database is to use ORDS Database Actions. Note that one must be using a 23ai database, otherwise the menu option in Database Actions does not appear. So, first step, login to Database Actions ( AKA SQL Developer Web ) https://my-autonomous-database.oraclecloudapps.com/ords/sql-developer and choose the Development->JavaScript menu option…
The Development->JavaScript section provides a handy UI for managing MLE Modules
If this is your first time defining an MLE Module the JavaScript area in Database Actions will look like this
In the MLE Editor there is a globe icon with an arrow pointing downwards. This is the Open from URL button. Press that, enter the URL https://cdn.jsdelivr.net/npm/mustache@4.2.0/mustache.mjs and press the Open button to load the text.
You should then see the source in the MLE Module editor. Enter MUSTACHE as the name ( leave the Environment info blank ) and press the Save icon to create the MLE Module.
Save the MLE Module source and call it MUSTACHE
The last part of that source, at line 764, the JavaScript exports a mustache object and that is what we will refer to in our own JavaScript: export default mustache;
Now, to use that MLE Module it must be included in an MLE Environment. Amongst other things, MLE environments are used for name resolution of JavaScript module imports. For this example we will define an MLE Environment called LIBRARY_ENV where we specify a namespace for modules we want to reuse.
Let’s switch to an SQL Worksheet environment for the rest of this article. Run this to create that environment…
With that in place, we’re now ready to create our JavaScript handler.
Developing the ORDS REST Service
Now, define a JavaScript handler that will query all records from HR.EMPLOYEES table and format the SQL results into an XML response using Mustache. The service will be part of the existing demo_mle_javascript module and available at employees/ so we will define a template and a handler:
Not much to explain about the above. We’re defining a pattern for a URI to add to an existing ORDS REST Service module and will have one or more handlers defined.
The parameters to the DEFINE_HANDLER procedure are more interesting so let’s take a look at them:
p_module_name => 'demo_mle_javascript', This states which module the handler belongs to.
p_pattern => 'employees/', This states which template in that module the handler belongs to.
p_method => 'GET', Handlers are define for a specific HTTP method. This handler will be for GET requests.
p_source_type => 'mle/javascript', Handler source can be of various types. Some simple such as a basic select query and some more complex such as pl/sql block. This handler's source is dynamic MLE JavaScript.
p_mle_env_name => 'LIBRARY_ENV', When dynamic MLE JavaScript imports a module it must state the MLE Environment where that module's namespace is defined. This is important for this handler's source and MUST be specified.
The p_source parameter content is more complex and deserves it’s own explanation. At a high level the steps are:
Define a query and execute it, storing the reference to the results in a variable called res
Import the Mustache MLE module and refer to it as mustache. Note that import is asynchronous so we wait for it to complete.
Defines a Mustache template for the XML structure. The curly bracket syntax is Mustache specific. Refer to Mustache documentation for more information.
Formats the data using Mustache and save that as a variable called output. Note that we reference the default object because that is what was in the export definition in the imported mustache.mjs
Sends the formatted XML as the HTTP response.
Remember that, as per ORDS REST JavaScript Handler developer guide documentation that the defined JavaScript code must be inside an anonymous function that receives the following two parameters: ORDS request object and ORDS response object. In the p_source parameter for this handler those two parameters are called req and resp respectively.
(req, resp) => { // define the query const query = 'select * from employees order by employee_id'; // execute it const res = session.execute(query); // import the mustache JavaScript library const mustache = await import('mustache');
// define the template var template = '<employees>{{#rows}} <employee id="{{EMPLOYEE_ID}}" first_name="{{FIRST_NAME}}" last_name="{{LAST_NAME}}" salary="{{SALARY}}"/>{{/rows}}</employees>';
// format the data from the result set var output = mustache.default.render(template, res);
// send the formatter XML as the HTTP response resp.content_type('application/xml'); resp.status(200); resp.send(output); }
Testing and Debugging
Testing the Endpoint
To test your new RESTful service, use tools like Postman or curl. Here’s an example using curl:
curl -X GET https://cust-23aidb.adb.eu-frankfurt-1.oraclecloudapps.com/ords/hr/demojs/employees/
Obviously your server hostname will be different. Also, you don’t have to be using the HR schema or have your ORDS REST Service module base path as demojs. So your URL could be way different but if you’ve gotten this far, you’ve already figured that out.
You should see an XML response formatted according to your Mustache template.
Debugging Common Issues
JavaScript Errors: Check for syntax errors or issues with the JavaScript handler.
SQL Query Problems: Ensure your SQL query is correct and returns the expected results.
Template Errors: Verify the Mustache template syntax and placeholders.
Conclusion
In this article, we’ve shown how to use Mustache and ORDS JavaScript handlers to transform SQL query results into beautifully formatted XML. By following these steps, you can enhance the presentation of your data and make it more accessible and readable.
About a year ago Oracle 23c Database Free became available as a preview of the new “converged database” which introduces a huge range of application developer concepts right in the database. Since then I’ve written a couple of articles about it in the context of the Oracle Developer DB Virtual Machine…
The latter covers upgrading your version of ORDS which is important for this article which will explore some Oracle Multilingual Engine ( MLE ) concepts which will require ORDS 23.4.0 ( or later ) just for the simplicity of the MLE editing support in Database Actions.
The goal here is to take Javascript based function which generates sample data and make that available as a REST service which will return data in a format which can be POSTed to the REST Enabled HR Employees table. Strictly speaking there’s no great advantage there because you could do that all in the DB anyway, but what gets covered here is the approach to take JSON data from Javascript and return it in the snake case format which can be used in Oracle REST Data Services.
Today we’re building on previous articles. In fact, the starting point is Jeff Smith’s Using JavaScript to create test data in Oracle Database 23c. Jeff’s blog post highlights all that Martin Bach’s article Using faker-js/Faker to generate test data respecting referential integrity in Oracle Database 23c delivers, showing how to follow Martin’s steps in Database Actions MLE JS editor. That can be run in your Oracle Developer DB Virtual Machine with ORDS 23.4.0. Of course you could just use the database in the VM and have ORDS 23.4.0 running outside it too. Either way, once you’ve gone through Jeff’s blog post you will have an MLE JavaScript module in place called MLE_FAKER and an MLE Call Specification called MLE_FAKER_API.
Therefore you can run this select statement to get a JSON object with random data for the HR Employee record:
Employee is a JSON object with random values but should be valid
The randomEmployee function uses camel case for field names
Responses for REST Enabled objects use snake case
You would think that you could use the generated JSON object as the payload body for a POST to the REST Enabled HR Employees table. The difference in naming convention for field names prevents that from happening. Send the mle_faker_api.random_employee JSON object to ORDS and you will get a HTTP 400 Bad Request response. Of course we could change the MLE_MAKER function implementation to use snake case. That would deviate from what Martin has implemented and he may have subsequent articles building on that implementation so it’s best to avoid interfering with that.
Camel to Snake – we’ll handle it
It is quite simple really, we write a PL/SQL block handler which will take the fields from the JSON object and define the OUT parameters using the field naming convention we want. The handler takes a gender parameter to pass to the call specification and defines the data types for the fields. The database JSON_VALUE function is key here…
DECLARE random_employee JSON; BEGIN select mle_faker_api.random_employee(:gender) into random_employee; :first_name := JSON_VALUE(random_employee, '$.firstName');
For your convenience here is a script to define the module/template/handler…
Which in turn can be used in a POST request to insert the record for real and generate an employee id.
The Employee JSON object is accepted and Tommie is employee number 221 !
Since one of the steps covered in Martin’s blog posts dealt with importing existing Javascript libraries ( Faker ) into the database, the transformation of the generated Employee JSON structure could possibly be achieved through Javascript libraries such as change-case. That’s a homework exercise for extra merit.
The real foundational work for this simple example of generating HR Employee sample data has been laid by Martin Bach. It was through building on that with a visual representation of the steps through ORDS’s Database Action UI that Jeff Smith brought it to my attention. I am grateful to both for the starting point they provided.