Logo REKA Advanced

Platform Knowledge Base

Comprehensive guides and documentation for designing, building, and managing enterprise applications on the REKA / LEAP platform.

Pre-requisites for Advanced Topics
  • REKA Beginner Level training completed.
  • Familiarity with HTML and JavaScript.
  • Understanding of backend (server) and frontend (UI) concepts.
  • Reference: REKA Glossary

Select a topic from the sidebar folder navigation to explore the detailed advanced functionalities of the REKA platform.

1. Form Design

1.1 Single Data

The Single Data option ensures that only one record exists for a given qualifier (criteria).

  • If no matching data exists → a new entry will be created.
  • If matching data exists → the existing entry will be loaded for update.

How to Enable Single Data

  1. Navigate to the form.
  2. Click the Edit Form button.
  3. In the Options tab, enable Single Data.
  4. Provide a Qualifier in JSON format.

Qualifier

The Qualifier defines the conditions used to check whether a record already exists.

Examples

1. One record per user

{ "$_.email": $user$.email }

2. One record per user per year

{ "$_.email": $user$.email, "$.year": new Date().getFullYear() }

3. Using $prev$ as a qualifier
If you want to tie a record strictly to a specific previous entry's ID:

{ "$prev$.$id": 12, "$.email": $user$.email }

Note: If an entry doesn't exist, a new entry will be created with previous data automatically mapping to $prev$.$id = 12.

Usage Notes
  • Applies only to special facets:
    • edit-single → update or create entry
    • view-single → view single entry
  • Does not apply to the standard add facet.
  • When Single Data is enabled, additional components (edit-single, view-single) will be available in the palette.
  • Equivalent behavior can be achieved by using edit with a qualifier parameter: /form/2123/edit?$_.email={{$user$.email}}
  • For more complex conditions, you can use the Custom Query Builder (supporting nested $and / $or logic). See Advanced Query Builder for details.

1.2 Previous/Next Form

Overview
This is a core REKA concept used to establish parent-child data relationships between form entries in a relational approach.

Key Rules:

  • One entry can only have one previous form to strictly prevent the diamond problem (cyclic dependency).

Configuration Steps

  1. Navigate to the Form.
  2. Click the Edit Form button.
  3. Open the Options tab.
  4. Select the Previous Form from the dropdown.
Important Notes on Cyclic Redundancy

Ensure the selected previous form:

  • Does not specify the current form as its previous form.
  • Does not specify a form that already references the current form.

Violations of these rules will cause cyclic redundancy errors.

Data Entry Behavior

Once a previous form is set:

  • You may choose to show previous form data when adding data in the current form.
  • Data for the current form can only be added in the context of a previous form’s data.
  • The previous entry must exist first.
  • The previous entry’s ID is mandatory to save the current form’s data.

Route for Adding Data

/form/<current-form-id>/prev?entryId=<id-of-previous-data>

You can also use parameters to identify the previous entry dynamically:

/form/<current-form-id>/prev?$.ic=901021...

Dataset & UI Behavior

  • You can declare prev as a facet and use it to control form state.
  • Dataset (standalone) for a form with a previous form: Will not show the Add button, even if the Add Action is allowed (because it lacks the previous context).
  • Dataset of a next form included in the current form: Automatically links using $prev$.$id = $.$id and filters by the prev data ID. The Add button will be shown.
Warning for Additional Parameters

If the included dataset requires additional parameters in its Source Init, "$prev$.$id": $.$id must be explicitly added alongside them.

One-to-Many Relationships

While a form can only have one previous form, one previous form may link to many next forms. For example:

  • Product → Order
  • Product → Review
  • Product → Document

1.3 Form Facets

Overview
A Facet is a named configuration that controls the state of form items within a form. This allows the same form to behave differently depending on the context (e.g., admin view vs. user view).

Form Item States

Each form item can be displayed in one of several states:

State Description
edit Form item is editable.
view Form item is read-only.
disabled Form item is disabled (non-interactive).
hidden Form item is hidden but still exists.
none Form item is completely removed.

Example Scenario: Admin-Edit Facet

  • Some items are set to view state (read-only for everyone).
  • Items that an admin can modify are explicitly set to edit state.
┌─────────────────────────┐       ┌─────────────────────────┐
│       Admin Facet       │       │       User Facet        │
├─────────────────────────┤       ├─────────────────────────┤
│                         │       │                         │
│  • Field A              │       │  Field A                │
│  • Field B              │       │  Field B                │
│  • Field C              │       │  ┌───────────────────┐  │
│  • Field C              │       │  │     Disabled      │  │
│                         │       │  └───────────────────┘  │
│                         │       │  Field C                │
└─────────────────────────┘       └─────────────────────────┘

Defining Facets

  1. Go to UI Editor → Forms → select the form you want.
  2. Click Edit Form.
  3. In the Edit Form dialog, open the Options tab.
  4. Under List of facets..., enter facet keys (comma-separated).

Configuring Form Items for a Facet

  1. Edit the form item.
  2. In the Form Item dialog, open the Options tab.
  3. Under Facet Mode, set the item’s state for each facet (default = edit).
  4. Click Save Item.

Configuring Sections for a Facet

You can also apply facet states at the section level. All items in the section inherit the section’s state, unless individually overridden.

  1. Edit the section.
  2. In the Edit Section dialog, open the Visibility tab.
  3. Under Facet Mode, set the section’s state for each facet (default = edit).
  4. Click Save Section.

Using and Opening a Form in a Facet

You can open a form with a facet by passing the facet key in the URL:

  • Normal form edit URL: /form/<form-id>/edit?entryId=<entryId>
  • Using a facet: /form/<form-id>/<facet-key>?entryId=<entryId>

Using Facets in a Dataset Action:

  1. Edit the Dataset Action.
  2. In Button Action, choose Open in Facet.
  3. Select the facet in the Next Facet dropdown.
  4. Click Save Action.
Scripting with Facets

In your custom form scripts, you can determine the currently active facet state by checking the value of the $action$ variable.

1.4 Extended Form

Overview
An Extended Form allows you to use another form to view or edit the entries of an existing (original) form.

  • The Extended Form inherits all items from the original form.
  • You can rearrange, add, or remove items from the inherited set.
  • Entries created in the original form can be viewed or edited through the Extended Form.

Usage

  • To use an Extended Form, simply change the formId in the URL.
  • In a dataset, you can open an entry in its Extended Form by selecting the Extended Form action.
Implementation Notes
  • Best practice: The easiest way to create an Extended Form is to clone the original form, then adjust (add, remove, or rearrange) the items as needed.
  • Any entry saved in an Extended Form remains linked to the original form.
  • Extended Forms do not have their own data; they only operate on the entries of their original form.
  • They are useful for:
    • Role-based layouts (e.g., admin vs. user forms)
    • Simplified views of complex forms
    • Context-specific editing without data duplication

Data Binding Behavior

┌──────────────────────────────┐                 ┌──────────────────────────────┐
│        Original Form         │                 │        Extended Form         │
│  ┌────────────────────────┐  │                 │  ┌────────────────────────┐  │
│  │ Field A                │  │ - - - - - - - -►│  │ Field A                │  │
│  │ Field B                │  │                 │  │ Field C                │  │
│  │ Field C                │  │                 │  │ Field D (added)        │  │
│  └────────────────────────┘  │                 │  └────────────────────────┘  │
└──────────────────────────────┘                 └──────────────────────────────┘
               │                                                │
               │                                                │
               └────────────────────────┬───────────────────────┘
                                        │
                                        ▼
                          ┌───────────────────────────┐
                          │     Dataset (Entries)     │
                          └───────────────────────────┘

When to Use Facet vs Extended Form

Use Facet when:
  • The differences are only in form item states (e.g., editable, disabled, view, hidden, none).
  • The form lifecycle scripts remain mostly intact (you can use $action$ to handle small changes).
  • The validation logic is mostly unchanged (you can use $form$.v to manage minor variations).
Use Extended Form when:
  • The form structure changes, such as different navigation type, or form items are rearranged into different sections.
  • You need to run different scripts for events (e.g., init function, on save, on submit).
  • The validation rules differ significantly between the forms.

1.5 Form Lifecycle & Items Control

A form provides several lifecycle events where you can run custom scripts. For multiline statements, always wrap the code in an IIFE (Immediately Invoked Function Expression).

Lifecycle Trigger Description
Init Function onInit() Runs on load.
On Save onSave() Runs after entry data is saved.
On Submit onSubmit() Runs after the entry data is submitted.
On View onView() Runs when the entry is opened in view mode.

Example: Reusing Init code in On View

(function(){
    $this$.isView = true;
    onInit(); // Call function directly to trigger outside of lifecycle
})()

Items Control (Field Behaviours)

  • Post-action: Triggered automatically when the field value changes.
  • Pre-requisite: Runs continuously on "ticks". It must return a truthy or falsy value to determine if the field is included/visible.

1.6 Pre-requisite

Pre-requisite scripts run continuously on "ticks" and must return a truthy or falsy value to determine if the field or section is included/visible.

Standard Field Syntax

Common syntax for checking different types of fields within the same context:

// Multiple lookup field
$.<multiple-lookup>.some(a => a.code == 'A')

// Normal lookup field
$.<lookup>.code == 'A'

// Normal field (Text, Number, Date, etc.)
$.<field> == <value>

Cross-Section Evaluation

To get a value from a Normal Section as a pre-requisite value in a Child Section:

$_.data.mod_staf.code == "PA"

Asynchronous Data Dependencies

If the condition depends on data from a RESTful endpoint, you cannot perform the fetch directly inside the pre-requisite script (as it runs continuously). Instead, load the data in a lifecycle event (like onInit) using $http$, $web$, or $endpoint$, and assign it to the $this$ object. Then, retrieve the value in your pre-requisite script.

1. Lifecycle (onInit) Script:

(function(){
  // load profile info
  $web$.get('https://io.ireka.my/api/entry/by-params?formId=5409', {
    params: {
      '$_.email': $user$.email
    }  
  }).subscribe(res => {
    // assign profile info to $this$.profile
    $this$.profile = res.data;
  });
})()

2. Pre-requisite Script:

Use optional chaining (?.) to prevent errors while the data is loading.

$this$.profile?.roles?.some(role => role.code == 'admin')

1.7 Evaluated Fields (EF) & Bindings

  • Bindings: Evaluated in real-time on the client-side and run on continuous "ticks".
  • Evaluated Fields (EF): The value for an EF is whatever is returned by the expression. Like lifecycle scripts, use an IIFE for multiline statements, and it must return a value.
  • PDF Exports: Use html-keepvalue in your markup if the evaluated value is needed during a PDF export.
  • Backend Evaluated Field (Be-EF): Use Be-EF to update prior data based on a newly evaluated field on the server side.

2. Dataset

2.1 Custom Query Builder

The Custom Query Builder allows you to define advanced, nested conditions for dataset queries using JSON. This gives you more flexibility than simple parameters.

  • The outermost condition is controlled by @cond (default: AND).
  • You can combine conditions using $and and $or, or by specifying parameter keys directly.

Steps to Add a Custom Query Builder

  1. Navigate to UI Editor > Datasets.
  2. Select the dataset you want to edit.
  3. Click the Edit Dataset button.
  4. In the Edit Dataset modal dialog, go to the Filters tab.
  5. Under Custom Query Builder, enter your query JSON.
  6. Click Save List.

Query Builder Rules

  • Keys can be:
    • $and → logical AND (accepts an array of conditions)
    • $or → logical OR (accepts an array of conditions)
    • A parameter key (e.g., $.name~contain)
  • Parameter keys in the query builder must match the dataset parameters, including decorators.
  • Each condition ($and, $or, or parameter) must be wrapped in its own object.
  • Outermost clause, value of $and and value for $or must be an array.

Example #1: Query Builder

[
  {
    "$and": [
      { "$.age~between": "" },
      { "$.gender.code": "M" }
    ]
  },
  {
    "$or": [
      { "$.email": "{{$user$.email}}" },
      { "$_.email": "{{$user$.email}}" }
    ]
  }
]

Parameter Value Resolution

In the Query Builder, a property’s value is only used if no parameter value or preset filter value is provided. If you know a parameter will always be supplied (via request or preset filter), you can safely set its value in the Query Builder to "" (empty string) or null.

Example #2: Passing values via HTTP request

?datasetId=222&$.age~between=12,50

(No need to pass $.gender.code, $.email, or $_email if already defined in the Custom Query Builder).

Example #3: Passing values via source init or _entry.dataset

{
  "$.age~between": "12,50"
}

2.2 Decorator Reference

Parameter Decorators modify how a query checks a field. Below is the mapping of field types to their available decorators.

Field type Available decorators
Lookup ~in, ~notin, ~contain, ~notcontain, ~from, ~to, ~between
Date, Number, Scale, ScaleTo5, ScaleTo10 ~from, ~to, ~between
Text ~in, ~notin, ~contain, ~notcontain

Parameter Decorators

Decorator Description Example Result
~in Field value is in provided list $.country.code~in="ID,MY,PH,SG" Country is one of ID, MY, PH, SG
~notin Field value not in list $.country.code~notin="ID,MY,PH,SG" Country is not ID, MY, PH, SG
~contain Field value contains string $.name~contain="Mohd" Name contains "Mohd"
~notcontain Field value does not contain string $.name~notcontain="Bill" Name does not contain "Bill"
~from Field value ≥ given number $.age~from=12 Age ≥ 12
~to Field value ≤ given number $.age~to=50 Age ≤ 50
~between Field value between two numbers $.age~between="12,50" Age between 12 and 50

Special Parameter Values

Value Description Example Result
~null Field value is NULL $.gender="~null" Gender is null
~notnull Field value is not NULL $.gender="~notnull" Gender is not null
$now$ Current timestamp $.timestamp~to="$now$" Entry timestamp ≤ now
$todayStart$ Today at 00:00 $.timestamp~to="$todayStart$" Entry timestamp ≤ today start
$todayEnd$ Today at 23:59:59 $.timestamp~to="$todayEnd$" Entry timestamp ≤ today end
Note

Filters configured via Edit Dataset → Filters do not include decorators by default (except for numeric fields). If a decorator is required, you can manually edit the key in the Preset Conditions section to add the appropriate decorator.

2.3 Intersection Query

The ~in operator handles array/list intersections. This is particularly useful when querying child collections or repeating fields. It allows you to match records that contain at least one overlapping value within a list or multi-valued field.

Example Data:

Entry 1: { "data": { "categories": [ { "code": "A" }, { "code": "B" } ] } }
Entry 2: { "data": { "categories": [ { "code": "B" } ] } }
Entry 3: { "data": { "categories": [ { "code": "A" }, { "code": "C" } ] } }
Entry 4: { "data": { "categories": [ { "code": "B" }, { "code": "C" } ] } }

Query:

$.categories*.code~in=A,C

Query Explanation:

  • $.categories* Iterates over all elements in the categories list.
  • .code Targets the code field of each category.
  • ~in=A,C Matches entries where any code value intersects with the set {A, C}.

Result:

The query returns entries whose categories list contains at least one matching code (A or C):

  • Entry 1 (A, B)
  • Entry 2 (B)
  • Entry 3 (A, C)
  • Entry 4 (B, C)

2.4 Filters, Preset Filters & Parameters

  • Precedence if conflict: Parameter > Preset Filter > List Filter
  • To structure a complex query, use the custom query builder.

Example:

[
  {
    "$or": [
      { "$.members*.email": "" },
      { "$.secretariat*.email": "" }
    ]
  },
  {
    "$.status": "active"
  }
]

2.5 Bulk Operations

Handling Selected Entries with $selected$:
When configuring a custom bulk action for a dataset, you can access the $selected$ object, which contains all the entries explicitly selected by the user in the UI.

The $selected$ object is structured as a dictionary (key-value mapping) where the key is the entry ID and the value is the entry data wrapper:

{
   '652010': { id: 652010, data: { /* data object */ } },
   '652011': { id: 652011, data: { /* data object */ } },
   '652018': { id: 652018, data: { /* data object */ } }
}

Example 1: Update all selected entries
To update a specific field (e.g., setting selesai to true) for all selected entries, iterate through the object keys and use the $update$ function.

(function(){
  Object.keys($selected$).forEach(id => {
     $update$(id, { selesai: true });
  });
})()

Example 2: Conditionally update selected entries
If you need to check a value before updating (e.g., only update if type is 'pelajar'), iterate through the object values to access the inner data payload.

(function(){
  Object.values($selected$).forEach(entry => {
     // Only update if the type is 'pelajar'
     if (entry.data?.type == 'pelajar') {
        $update$(entry.id, { selesai: true });
     }
  });
})()

2.6 Dataset Endpoints & Query Syntax

You can query dataset endpoints directly via URL parameters.

  • Standard Query: Add parameters using the usual $.field-code.
    .../list?datasetId=1168&$.city.code=KCH
  • Range Queries: Use decorators for number/date/scale.
    .../list?datasetId=1168&$.age~between=10,16
  • Array/Child Queries: Use * to query within multiple options.
    .../list?datasetId=116&$.joblist*.company=SACOFA
  • Permissive Query (OR): By default, query params are restrictive (AND). Add @cond=OR for any-match.
    .../list?datasetId=116&$.type.code=Stud&$.company=SACOFA&@cond=OR
  • Sorting: Use the sorts parameter.
    .../list?datasetId=116&sorts=$.birthdate~desc,$.name~asc

3. Dashboard

3.1 Advanced Charting: Multi-Category vs. Multi-Value

Understanding the distinction between configurations will help you choose the right visualization.

  • Multi-Category Charts: Segments a single numerical metric by two or more descriptive attributes (e.g., clustered groupings in Bar charts, multi-pie grids in Pie charts). Drill-Down is disabled.
  • Multi-Value Charts: Tracks multiple distinct numerical calculations against a single category axis (e.g., plotting Total Revenue vs Operating Costs on a Dual Y-Axis). Drill-Down is disabled.
Tip

If your primary goal is to allow users to click into a chart and view the underlying raw data (Drill-Down), stick to a Standard Chart (one category, one value).

3.2 Understanding Axes and Series

  • X-Axis (xAxis): The horizontal axis (Categories).
  • Y-Axis (yAxis): The vertical axis (Values).
  • Series: The actual data plotted (bars, lines, pie slices).

3.3 Customizing Charts with ECharts Options

Add native ECharts options in Options > Additional options for chart. REKA will merge (and overwrite) your provided options with the defaults.

Example: Adding a DataZoom Scrollbar

{   
  "series": [{ "name": "Bilangan Peserta" }],
  "dataZoom": [ 
    { "type": "inside", "start": 50, "end": 100 },
    { "show": true, "type": "slider", "y": "90%", "start": 50, "end": 100 }
  ]
}

3.4 Transforming Chart Data

You can modify datasets before rendering using Options > Transform Function.
For quick category or value mapping, use the built-in $eachName$ and $eachValue$ shortcuts:

  • Rename categories: $eachName$(i => 'Zone ' + i)
  • Transform values: $eachValue$(i => i * 5)

Full Script Transformation:
Data for multi-value/category charts arrives as a multi-dimensional array (matrix). Ensure your script iterates rows and columns appropriately.

(function(){
  let remapData = [...$dataset$]; 
  if (remapData.length > 0) {
    // 1. Fix header row
    remapData[0] = remapData[0].map(h => h === 'n/a' ? "No Data" : h);
    // 2. Fix category column
    for (let i = 1; i < remapData.length; i++) {
      if (remapData[i][0] === 'n/a') remapData[i][0] = "No Data";
    }
  }
  return remapData;
})()

3.5 Restful Source Charts

Charts can be generated using data from restful endpoints instead of standard datasets. Ensure the endpoint returns data in the correct JSON array structure expected by the charting engine.

3.6 Enable Drill-Down List

To enable drill-down capabilities for a chart:

  1. Go to Options > Enable Drill-down.
  2. Select Columns: Choose the columns to display (limited to fields from a normal section).
  3. View Entry Button: Check 'Enable Drill-down View Entry' to allow users to open the full record.
Warning

Drill-down will not work for Multi-category or Multi-value charts, as these visualize aggregated data intersections rather than a single database row.

3.7 Chart Parameters & Decorators

Just like Datasets, Charts in REKA also support the use of decorators (~decorator) in their parameters to dynamically filter the underlying data before it is visualized.

This means you can apply the exact same parameter decorators—such as ~in, ~notin, ~contain, ~from, ~to, and ~between—directly to a chart's data source query.

Example Usage:
If you want a chart to only render data for specific categories (e.g., filtering a chart to only show the IT and HR departments), you can pass a parameter to the chart like so:
$.department~in=IT,HR

Note

For a complete list of available decorators and how they function, please refer back to the 2.2 Decorator Reference in the Dataset section.

4. Lambda & Real-time in REKA

4.1 Overview

Lambda is a script runner in REKA that executes on the backend using JavaScript and polyglot Java API access.

  • Lambdas execute on separate threads, making them ideal for long-running processes.
  • A Lambda can only access data within its own app.

4.2 Development & Default Objects

Each Lambda automatically provides access to the following objects:

Object Description
_request Represents the HTTP request object.
_response Represents the HTTP response object.
_out Used for building JSON responses. Example: _out.put("key", value);
_param Provides access to request parameters.
Tip

Always prefer _param instead of _request.getParameter(). _param works consistently whether the Lambda is triggered by a standard HTTP request or by a Cogna function call.

Handling Request Bodies

To get the raw request body in a Lambda (such as from a POST request), use _param._body. This will return a string. To convert it into a usable JSON object, you should parse it:

var requestData = JSON.parse(_param._body);

Available Endpoints generated by Lambda

Endpoint Type Description
Endpoint Returns JSON (_out.put(key, value))
Print / Print PDF Returns text/PDF from print()
Stream Streaming output for long-running processes
Web Viewer Opens output in UI viewer.

4.3 Common Use Cases for Lambda

  1. Scheduled Jobs: Run tasks automatically (cron-like).
  2. API Integrations: Use _http.GET and _http.POST.
  3. Data Validation: Run custom rules before entry saves.
  4. Cogna Integration: Register Lambda as an AI tool.

4.4 Web Scraping with Lambda (Jsoup)

In addition to _http.GET and _http.POST for making HTTP requests, REKA provides built-in support for Jsoup, a Java library designed for working with real-world HTML.

This allows you to:

  • Scrape web pages directly.
  • Parse and traverse HTML documents.
  • Extract structured data using familiar CSS-style selectors.
  • Integrate real-time web content into Cogna workflows.

How It Works

Binding: Add the Jsoup binding in Lambda. Once added, you can access Jsoup functions via the _jsoup object.

Core Methods Available:

  • _jsoup.connect(url) → Creates a connection to the specified URL (returns a connection object for configuring headers, cookies, POST data, etc.).
  • _jsoup.parse(html) → Parses an HTML string into a document.
  • _jsoup.parseBodyFragment(htmlFragment) → Parses a fragment of HTML.

Selectors: You can use standard CSS-like selectors with doc.select() to extract elements from the HTML.

Example

// Connect to Wikipedia and fetch the document
var doc = _jsoup.connect("https://en.wikipedia.org/").get();

// Select news headlines from the "In the news" section
var newsHeadlines = doc.select("#mp-itn b a");

// Print out title and link
newsHeadlines.forEach(headline => {
    print(`
        ${headline.attr("title")}  
        ${headline.absUrl("href")}
    `);
});

Output (example):

NASA launches Artemis mission
https://en.wikipedia.org/wiki/Artemis_program

2024 Summer Olympics open
https://en.wikipedia.org/wiki/2024_Summer_Olympics

Usage with Cogna

This Lambda function can be registered as a tool in Cogna, allowing your LLM workflows to access and query real-time web data.

Notes and Limitations
  • JavaScript-rendered content: Jsoup only fetches raw HTML. It cannot see content dynamically loaded with JavaScript (e.g., in SPA applications like React/Angular).
  • Building requests: _jsoup.connect(url) returns a connection object where you can set headers, user-agent, cookies, or POST data before calling .get() or .post().
  • Selectors: doc.select("css-selector") supports the same syntax as CSS query selectors. Example: doc.select("div.article p:first-of-type").

_http vs _jsoup in REKA Lambda

Feature / Use Case _http.GET / _http.POST 🛰️ _jsoup 🕸️
Purpose Fetch raw response (JSON, XML, HTML, text, binary). Scrape and parse HTML pages into a structured DOM-like object.
Best for APIs, structured data, files, JSON-based endpoints. Websites with static HTML where you need to extract elements.
Output Raw string or binary response. Parsed HTML document (Document object).
Traversal Manual string parsing or regex required. Native support for CSS selectors (doc.select("div > a")).
Selectors Not available. Powerful CSS-like queries.
Handling JS-rendered content (SPA) Not supported. Not supported (same limitation).
Ease of extracting links, text, attributes Requires manual parsing. Built-in methods (.text(), .attr(), .absUrl()).
Follow Redirect Behaviour Normal follow redirect. Don't follow redirect.
When to use REST APIs
JSON endpoints
Full webpage request
Web scraping
Parsing HTML fragments
Extracting structured data from pages

5. Real-time Notification

REKA supports multiplexed connections using WebSockets to establish real-time pub/sub communication between clients.

5.1 Client-Side (UI) Usage

  • Available in both Forms and Custom Screens.
  • Subscribe: $live$.watch('channel-name', callback)
  • Broadcast: $live$.publish('channel-name', payload)
Warning

Ensure that your channel is unique to prevent unintended conflicts with other applications or environments. It is highly advisable to include the App ID in the channel key (e.g., channel_update_${$app$.id}).

5.2 Backend (Lambda) Usage

$live$ is also available in Lambda using the _live object. But first, you have to include Live in the Lambda Bindings configuration.

Then, use _live.publish to publish a live notification from Lambda to the listening client (usually a form or screen using $live$).

Lambda Script Example:

// ensure to stringify the message before publish. 
_live.publish(['updated-ticket-info-' + entryId], JSON.stringify(updatedTicket));

Client Listener Example:
On the listening client, parse it back to an object using JSON.parse().

$live$.watch(['updated-ticket-info-' + $.$id], res => {
    let parseObj = JSON.parse(res);
    // Assign to local variable or scope
    $_ = parseObj;
});

6. Integrations & Endpoints

6.1 Enabling a Custom Domain

You can use a reverse proxy to route traffic from a custom domain (e.g., app.example.com) to your REKA subdomain app-path.ireka.my.

Apache (.htaccess)

Requires mod_proxy and mod_headers.

<IfModule mod_rewrite.c>    
    RewriteEngine On    
    # Forward the original host expected by the Reka platform    
    <IfModule mod_headers.c>        
        RequestHeader set Host "app-path.ireka.my"    
    </IfModule>    
    # Reverse proxy all requests to the Reka application    
    RewriteRule ^(.*)$ https://app-path.ireka.my/$1 [P,L]
</IfModule>

Nginx

Use proxy_pass and proxy_set_header Host in your server block.

server {    
    listen 443 ssl;    
    server_name mydomain.com;    
    location / {        
        proxy_pass https://app-path.ireka.my/;        
        proxy_set_header Host app-path.ireka.my;        
        proxy_set_header X-Real-IP $remote_addr;        
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;        
        proxy_ssl_server_name on;    
    }
}

6.2 Telegram Bot Setup x Reka

You can integrate Telegram bots with REKA to send automated notifications or receive user inputs via Webhooks. This requires setting up a bot via BotFather and using Lambda's _http bindings.

1. Create a Bot via BotFather

  1. Access BotFather: Open Telegram and search for @BotFather or visit t.me/BotFather.
  2. Start Interaction: Type /start to view available commands.
  3. Create New Bot: Type /newbot.
  4. Set Bot Name: Enter a display name for your bot (e.g., REKA Automation Bot). Spaces are allowed.
  5. Set Bot Username: Enter a unique username that ends with the word bot (e.g., reka_automation_bot). Spaces are not allowed.
  6. Save Bot Token: BotFather will generate a unique token (format: <bot_id>:<secret_token>). Keep this secure, as it is required to interact with the Telegram API.

2. Integrating with REKA Lambda

You can use Lambda's _http.POST and _http.GET methods to interact with the Telegram API. Note: Ensure the HTTP binding is enabled in your Lambda configuration.

Example A: Sending a Message (POST)

// Konfigurasi token dan ID chat
var botToken = "<BOT_TOKEN_ANDA>";
var chatId = "<CHAT_ID_PENGGUNA>";
var url = "https://api.telegram.org/bot" + botToken + "/sendMessage";

// Payload data
var payload = {
  chat_id: chatId,
  text: "Halo! Ini adalah pesan otomatis dari REKA Lambda."
};

// Mengirim HTTP POST request ke Telegram API
var res = _http.POST(url, { 
  headers: { "Content-Type": "application/json" }, 
  body: payload 
}, "json").body();

print(res);

Example B: Registering a Webhook (GET)

To receive incoming messages from Telegram into REKA, you must register an endpoint as a webhook.

// URL Endpoint External Manager REKA anda
var rekaWebhookUrl = "https://app-path.ireka.my/api/endpoint/webhook-id";
var botToken = "<BOT_TOKEN_ANDA>";
var url = "https://api.telegram.org/bot" + botToken + "/setWebhook?url=" + rekaWebhookUrl;

// Mengirim HTTP GET request untuk mendaftarkan webhook
var res = _http.GET(url, { headers: {} }).body();

print(res);
Handling Incoming Webhooks

Once the webhook is registered, Telegram will send a JSON payload to your REKA endpoint whenever a user interacts with the bot. You can use _param._body in your Lambda to parse and process the incoming messages.

6.3 Integrating REKA into Mobile Apps (WebView)

Overview
REKA applications can be integrated into native mobile apps using a WebView. This approach allows you to reuse the full REKA web application or embed only specific components (forms, datasets, dashboards) inside your mobile app.

1. WebView Prerequisites & Settings

Before loading a REKA app in a WebView, the following capabilities must be enabled to ensure full functionality:

  • File upload: Required for attachments, image uploads, and document uploads in forms.
  • Camera access: Required for camera capture fields and image upload via camera.
  • Geolocation: Required for location-based form fields and services.
  • Service Worker: Required for offline support, caching, PWA-related features, and performance optimization.
Important

If Service Worker is disabled, some REKA features (offline mode, caching, background sync) may not function correctly.

2. Integration Options

You can integrate REKA in two main ways, depending on your use case.

Option A: Integrate the Entire REKA App

This embeds the full application, including the app shell, navigation, sidebar, and all modules. Load the URL directly into the WebView:

https://<app-name>.ireka.my/

Recommended when you want a complete REKA experience, navigation is required, and the mobile app is mainly a container.

Option B: Integrate Only a Specific Component

This approach embeds only a specific page (Form, Dataset, Dashboard). Disable the App Shell in the URL (if supported) and navigate directly to the target component URL.

  • Form (Add/Edit/View): https://<app-name>.ireka.my/form/<form-id>/add
  • Dataset: https://<app-name>.ireka.my/dataset/<dataset-id>
  • Facet-based Form: https://<app-name>.ireka.my/form/<form-id>/<facet-key>?entryId=<entry-id>

Suitable when the mobile app controls its own navigation, only selected features are needed, and you want tighter native UI control.

3. Authentication Handling

REKA supports URL-based authentication for WebView integration.

Method A: Authentication Using Access Token

Use when authentication is handled by an external Identity Provider (IdP) and the mobile app already has an authenticated session.

  1. Authenticate the user in the mobile app using a supported provider (e.g., Google, Azure AD).
  2. Obtain a valid access token.
  3. Append the token and provider name to the REKA app URL.
https://<app-name>.ireka.my/?accessToken=<ACCESS_TOKEN>&provider=<PROVIDER>

Note: The token must be valid and unexpired. The provider name must match the configured provider in REKA. Tokens are typically exchanged for a REKA session on first load.

Method B: Authentication Using API Key

Use for system-to-system access, service accounts, or kiosk/controlled-device scenarios.

  1. Open the REKA app in App Designer.
  2. Navigate to UI Editor > Manage API Key and create a key.
  3. Append the API key to the WebView URL.
https://<app-name>.ireka.my/?apiKey=<API_KEY>

Note: API keys are app-scoped. Accessing resources from another app requires that app’s API key. They should be treated as secrets and never hardcoded in production apps.

4. Mobile Platform Examples

Android (WebView)
WebView webView = findViewById(R.id.webview);

WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
settings.setMediaPlaybackRequiresUserGesture(false);

webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient());

webView.loadUrl("https://hr-system.ireka.my/?apiKey=abc123xyz");
  • Handle file upload using onShowFileChooser.
  • Enable location permission in AndroidManifest.xml.
  • Grant camera permission at runtime (Android 6+).
iOS (WKWebView)
let config = WKWebViewConfiguration()
config.preferences.javaScriptEnabled = true
config.websiteDataStore = .default()

let webView = WKWebView(frame: view.bounds, configuration: config)
view.addSubview(webView)

let url = URL(string: "https://hr-system.ireka.my/?accessToken=XXXX&provider=google")!
webView.load(URLRequest(url: url))
  • Enable camera and microphone permissions in Info.plist.
  • Enable file upload via WKUIDelegate.
  • Allow geolocation access via CoreLocation.

5. Security & Best Practices

  • Always use HTTPS.
  • Prefer access_token over API key for end-user authentication.
  • Store tokens securely (Keychain / Keystore).
  • Do not hardcode API keys in mobile apps.
  • Use short-lived tokens where possible.
  • Restrict API key permissions to minimum scope.
  • Consider backend token exchange instead of passing tokens directly to WebView.

6. Common Integration Pitfalls

Issue Cause Resolution
File upload not working File chooser disabled Enable file access & WebChromeClient
Camera not opening Permission missing Enable runtime permissions
Blank page Service Worker disabled Enable service worker support
User logged out on refresh Token expired Refresh token before reload
Cross-app access denied Wrong API key Use API key from target app
Summary

REKA WebView integration allows mobile apps to embed full applications or individual components, reuse existing workflows, and leverage REKA authentication via tokens or API keys. With proper WebView configuration and secure authentication handling, REKA apps can be seamlessly integrated into Android and iOS applications.

6.4 3rd Party Integration Methods

  • Backend Data (Inbound/Outbound): Achieved via Restful API using $http$, $endpoint$, or custom Lambda functions.
  • Frontend Library Integration: External JS libraries can be injected directly into forms/screens using the $loadjs$ utility.
  • Tooling (Reporting/Analytics): Can consume data from dataset endpoints, chart endpoints, or custom Lambdas.

6.5 External Endpoints Manager

Use the Endpoint Manager to register and securely manage external API endpoints centrally.

  • Authentication: Native support for OAuth2 flows.
  • Execution: Can be called via $endpoint$ on the frontend or _endpoint in Lambda.
  • Parameters: URL parameters can be unboxed dynamically using placeholders {} populated by a provided JSON object payload.

File Blob / Byte Array Handling

If an external endpoint returns a file (blob), set the configuration to Response Type: Byte Array to prevent file corruption.

  • Frontend: Convert the byte array to a file object using blob:base64.
  • Lambda: Write the byte array directly to the response output stream:
var out = _response.getOutputStream();
out.write(byteArray);

7. Lookups

7.1 Database vs Restful Lookups

REKA allows fields to pull data from either internal Database Lookups or external Restful API Lookups. Each requires specific configurations for data mapping.

Restful Lookup Configuration

  • Endpoint URL: Supports placeholders {} that are dynamically unboxed with values passed via parameters.
  • Root: Points to the specific JSON path that contains the array or list of data to be iterated.
  • Code, Name, Extra: Points to the specific JSON paths for these respective values (this path should be relative to, or excluding, the Root path).

Additional Data Fields

How additional data is handled depends on the type of lookup being used:

Lookup-Database:

  • You must specify additional fields as a schema.
  • To specify the data type, use the format: field_name:type
  • Supported types: text, longtext, number, date, file, options, lookup, multiplelookup.

Example for staff list lookup:

designation:text,
age:number,
photo:file,
department:lookup:4123

Lookup-Restful:

  • You can leave the schema configuration blank to automatically include all remaining JSON fields as additional data.
  • To explicitly remap, rename, or flatten deep JSON fields, use the @ syntax.

Example to map staff list from REST endpoint to additional data fields:

designation@/data/designation/name,
age:number@/data/umur,
department@/data/department/name

General Syntax to Map Fields

Whether remapping a Restful lookup or structuring a Database lookup, use the following general syntax:

field_name:type@/path/to/value
Advanced Type Configurations (3rd Parameter)
  • For type options, you can provide the options as a list using a pipe-separated string as the 3rd parameter. Example: gender:options:Male|Female
  • For type lookup or multiplelookup, you can provide the lookup ID as the 3rd parameter. Examples: department:lookup:4121 or certification:multiplelookup:4125

7.2 Lookup Query Endpoints

You can pass parameters directly into Lookup Query Endpoints to filter lists.

  • Database Lookup: .../lookup/5108/entry?extra=Staff&$.gender=Male (Can be filtered using Code, Name, Extra, or $.<additional-field>).
  • Restful Lookup: Only supports parameters specifically supported by the source API endpoint.

If a Restful lookup requires a POST request with parameters in the body, pass a postBody array literal:

{
  "postBody": [{"paramName": "co_id", "paramValue": $.cp_course_id.extra}]
}

8. REKA Templating & Screens

8.1 Formatting Pipes

REKA provides formatting pipes ({{val|pipe}}) to transform data directly on the view before rendering.

Pipe Usage Description / Format
{{$.date_field|date:"DD-MM-YYYY"}} Custom Date string formatting.
{{$.attachment|src:inline}} Inline display (Streamable video/audio: src:stream)
{{$.filing_no|qr}} Generates base64 QR Code image source.
{{$.amount|number:"1.2-3"}} Number format: minInt.minFrac-maxFrac

8.2 Mailer Templating

The Mailer module uses the StringTemplate (ST) v4 engine for text generation.

Conditional Logic

Conditional rendering is supported using ST v4 syntax.

{{if ($._outside_sarawak)}}  
  is A  
{{else}}  
  is B  
{{endif}}

Iteration (Each Loop)

Iterate over collections using the ST v4 loop syntax.

{{ $.userlist:{user |  
  {{i}}. {{user.name}} ({{user.email}}) 
} }}

Where:

  • userlist is a collection.
  • user is the loop variable.
  • i is the iteration index.

Field Formatters (Inline Transformations)

Formatter syntax Description
{{$.date;format="date:dd/MM/yyyy HH:mm"}} Date formatter
{{$.number;format="number:%32.12f"}} Number formatter (Uses Java String Formatter)
{{$.text;format="string:upper"}} Make uppercase
{{$.text;format="string:lower"}} Make lowercase
{{$.text;format="string:cap"}} Make first-cap
{{$.text;format="string:url-encode"}} Encode URL
{{$.text;format="string:xml-encode"}} Encode XML
{{$.file;format="src"}} Upload file URL
{{$.file;format="qr"}} QR image src

Sending Email Using Lambda

1. Send Email Directly

_mail.send({ 
  to: "<to>", 
  cc: "<cc>", 
  bcc: "<bcc>", 
  subject: "<subject>", 
  content: "<content>" 
}, _this);

Example:

_mail.send({
  to: "applicant@example.com",
  cc: "hr-dept@example.com",
  bcc: "audit@example.com",
  subject: "Application Received",
  content: "<h1>Thank you!</h1><p>We have received your submission.</p>"
}, _this);

2. Send Email Using Template

_mail.sendWithTemplate("<mailer-id>", entry, _this);

Where:

  • <mailer-id> is the predefined mailer template ID.
  • entry is the data context bound to the template.
  • _this represents the Lambda execution context.

Example:

// Create or fetch the data context to bind to the template
var entryData = {
  data: {
    name: "Ahmad",
    reference_no: "REQ-9921",
    status: "Approved"
  }
};

// Send using a predefined template created in the UI Editor
_mail.sendWithTemplate("approval_notification_tmpl", entryData, _this);

8.3 Custom Screens

Custom screens allow you to build completely bespoke interfaces inside the REKA app shell wrapper.

  • Capabilities: Supports native plugins like the QR scanner integration.
  • Scripting Scope: Logic can be scoped to the Entry Page context, Entry List, or global Static Page contexts depending on the screen type selected.

9. App Structure & Global Variables

9.1 Global $conf$ Object

$conf$ is a state object used to declare app-scoped global variables that persist throughout the user session.

  • The values stored in $conf$ are accessible across all components, such as forms, datasets, and custom screens.
  • To initialize and declare values for $conf$ immediately when the app starts, place your logic inside the Structure Init Function.
// Example in Structure Init
$conf$.deptCode = "HR_01";

Async Initialization & Change Detection

The App Structure Init function also supports asynchronous operations. This is highly useful when you need to fetch global data (like user profiles or lookup dictionaries) via API before rendering dependent UI components.

Example:

(async function(){
  let profileRes = await $web$.get('https://io.ireka.my/api/entry/by-params?formId=5473', {
    params:{
      '$.email': $user$.email
    }  
  });

  $conf$.profile = profileRes.data;
  $digest$();
})()
Important Notes
  • Note the keyword async in the IIFE (Immediately Invoked Function Expression) and the use of await for the $web$.get request.
  • If the value of $conf$.profile (or any asynchronously fetched global variable) is used directly in the UI template, it is a good idea to call $digest$() after setting the value to ensure the change detection is fired up and the interface updates immediately.