Welcome to the official documentation and advanced technical guide for Flowork Neural Builder. This article is designed as a comprehensive reference (masterclass) for developers, automators, and system architects looking to maximize the potential of Flowork OS by creating Custom Modules or *User-Defined Functions* (UDF).
In this extensive guide, we will dissect in depth what Flowork OS is, how Edge and Client-Side computing architectures work, and provide a step-by-step process for assembling your own custom JavaScript modules from scratch into ready-to-use drag-and-drop nodes.
CHAPTER 1: Understanding the Flowork OS Ecosystem
Before we dive into writing code, it is crucial to understand the philosophy and underlying architecture of the platform you are using.
1.1 What is Flowork OS?
Flowork OS is not just a traditional visual workflow builder. Generally, automation platforms like Zapier, Make (Integromat), or flowork rely heavily on heavy backend servers (Node.js/Python) to execute every node or logic module. This results in latency (time lag), ballooning server costs, and data privacy concerns because your payload must traverse third-party servers.
Flowork OS breaks these boundaries by introducing an Edge-Native and Client-Side Execution architecture. Built on a Vue.js foundation and hosted via Cloudflare Pages' infrastructure—spread across hundreds of data centers worldwide—Flowork OS shifts the computational load directly to the user's browser (your local RAM and CPU) or to the Edge network closest to your location.
Advantages of This Architecture:
1. Zero-Latency Execution: Module execution (such as data transformation, mathematical calculations, or text formatting) happens in milliseconds because it is executed directly by the JavaScript Engine (V8) in your browser. 2. Privacy by Design: Your sensitive data is never stored or processed on our servers. All logic execution happens locally. 3. Decentralized Resources: There is no server downtime due to high traffic loads, as every user's browser acts as its own execution "server."
1.2 The Concept of Dynamic Edge Modules (User-Defined Functions)
By default, Flowork Neural Builder provides various built-in (system) modules such as HTTP Request, Telegram Trigger, Webhook, and more. However, the needs of every business or developer are unique and specific. It is impossible for Flowork's core developers to provide millions of modules for every possible API or business logic in the world.
Therefore, we created the Custom Module feature. This feature allows you—without needing deployment processes, GitHub pushes, or server builds—to write your own backend logic directly within the browser.
How does it work technically?
Flowork utilizes an On-The-Fly Compiler technique. When you write raw JavaScript code (as text/string) in the module builder interface, the *FlowRunner* engine (the brain of Flowork) uses JavaScript's built-in `new AsyncFunction()` constructor. This technique dynamically injects, compiles, and transforms your code text into a native asynchronous function that runs in memory (RAM) instantly. This makes Flowork more than just a standard web app; it is a *Platform as a Service (PaaS)* running purely in a client environment.
---
CHAPTER 2: Anatomy and Creation of Custom Modules
Now that you understand the power behind the scenes, let's start building your first custom module.
2.1 Accessing the "Build Module" Interface
The module creation process does not require additional app installations or third-party IDEs (Integrated Development Environments). Everything is integrated within the Flowork Workspace.
1. Open the Flow Designer dashboard in your web application. 2. Look at the main navigation menu (Header) at the top of the screen. Among the action buttons (such as Import, Export, Share), you will find an exclusive button labeled "🛠 Build Module". 3. Click that button. A *Custom Module Creator* pop-up (Modal) will appear. This is your creative canvas.
This interface is divided into several key sections that define your module's identity and logic behavior.
2.2 Module Visual Identity (Metadata)
The top part of the form is dedicated to visual identity. This is vital so your module is easy to find in the left sidebar and looks professional if you share this workflow with other users.
* Module Name (Display): This is the primary name of your module. Keep it concise yet representative. Avoid overly long names that might break the UI layout. * *Good example:* `Tax Calculator`, `WA Number Formatter`, `JSON Extractor`. * *Bad example:* `Module For Calculating Eleven Percent Value Added Tax`.
* Module Icon (Emoji): Flowork takes a modern approach by using standard Unicode Emoji characters as module icons. This ensures lightning-fast rendering without needing to load external SVG or PNG image files. Choose an icon that fits the function. * *Examples:* 🧮 for math, 📝 for text, 🤖 for AI, 🌐 for networking.
* Description: Provide a short description (maximum 2 sentences) of what this module does. This description will appear under the module name in the sidebar list and helps others understand the node's function without looking at the code.
2.3 Parameter Schema (UI Input Engine)
This is one of the most advanced features of Flowork Custom Modules. Instead of making you create your own HTML/CSS UI for your module, Flowork uses the concept of JSON Schema Driven UI.
You simply write the definitions of what inputs your module needs in a pure JSON Array format. The Flowork UI engine automatically (via Vue.js reactivity) translates that JSON into an elegant HTML form (text inputs, dropdowns, or other options) in the Properties panel (right sidebar) when the node is clicked.
JSON Schema Writing Rules:
1. Must start with an open square bracket `[` and end with a closed one `]`. 2. Contains JSON objects `{ ... }` separated by commas `,`. 3. Use double quotes `"` strictly for both keys and values (as per strict JSON standards, not just standard JavaScript objects).
Mandatory Properties in Each Parameter Object:
* "name": This is the *unique ID* of the parameter. This name must not contain spaces, should not start with a capital letter (camelCase or snake_case is recommended), and must not contain special characters other than underscores. This variable is what you will call inside your JavaScript logic code. * "displayName": This is the text label (human-readable) that users will see above the input box. You are free to use spaces and capital letters here. * "type": Determines the form input type. * Use `"string"` for a standard text input (like ``). * Use `"options"` for a dropdown menu (like a `
Here is an example of a complete JSON parameter schema implementation for a Mini Calculator module:
```json [ { "name": "number1", "displayName": "First Number", "type": "string", "default": "10" }, { "name": "number2", "displayName": "Second Number", "type": "string", "default": "5" }, { "name": "operation", "displayName": "Select Operation", "type": "options", "options": ["add", "subtract", "multiply", "divide"], "default": "add" } ]
```
When you save the configuration above, Flowork will render two text boxes (for number inputs) and one dropdown (containing 4 math operation choices). You don't need to write a single line of HTML/Vue code; our engine handles it reactively.
---
CHAPTER 3: Writing Execution Logic (JavaScript)
This section is the core engine of your custom module. Here, you write the data processing logic using standard ES6+ JavaScript. This code is executed within an `async` function, so you are free to use `await` if you need to fetch external APIs (HTTP Requests).
3.1 Understanding the Execution Environment (Sandbox)
When your code is executed, the *FlowRunner Engine* injects two primary variables into your local scope. You must understand these two variables:
1. `node` (Object): Represents the overall data of your own module. The most important variable inside this object is `node.data.config`. This is where all the UI parameter values (filled by the user in the right panel) are stored. * *Example:* If you created an input named `"number1"` in the JSON schema, its value can be accessed via `node.data.config.number1`.
2. `inputData` (Object | null): Represents the data stream (*payload*) sent by the previous module (if your module is connected by an edge from another node). If your module is a Trigger (the first node), this value is usually empty or comes from a webhook.
3.2 Writing the Calculator Code
Let's translate our Mini Calculator logic into JavaScript. Copy the following code into the Execution Logic box:
```javascript // 1. PARAMETER EXTRACTION // We take the values filled by the user in the UI panel. // Since form inputs are always strings, we use parseFloat to convert them to Numbers. const val1 = parseFloat(node.data.config.number1 || 0); const val2 = parseFloat(node.data.config.number2 || 0); const operation = node.data.config.operation || "add";
// 2. MAIN LOGIC PROCESS let result = 0;
if (operation === "add") { result = val1 + val2; } else if (operation === "subtract") { result = val1 - val2; } else if (operation === "multiply") { result = val1 * val2; } else if (operation === "divide") { if (val2 === 0) { throw new Error("Division by zero is not allowed."); } result = val1 / val2; } else { throw new Error("Unknown operation: " + operation); }
// 3. RETURN DATA (OUTPUT) // You MUST return a JSON object. // This object will be passed to the next node in the workflow. return { status: "success", message: "Calculation successfully executed at Edge", details: { operation_performed: operation, input_numbers: [val1, val2] }, final_result: result };
```
Golden Rules for Logic Writing:
* Use `throw new Error("Message")`: If a fatal error occurs (e.g., validation fails), use `throw`. The Flowork engine will catch it automatically, mark the module with a `failed` status (red color), and cancel subsequent node executions to prevent chain errors. * Always Return an Object: The data you return must be a JSON object `{}`, not a single primitive type (like just returning the number `15`), because subsequent modules usually expect an object to map parameters.
---
CHAPTER 4: Module Management and Lifecycle
After you press the "➕ Save Module" button, what actually happens behind the scenes?
4.1 Persistence via Local Storage
Flowork OS adheres strictly to "Serverless" principles. Therefore, your JSON schema and JavaScript code lines are saved persistently within your browser's *Local Storage*.
* If you refresh the page (F5), your module will not disappear. * The module will immediately appear in the left sidebar below the search bar, merging seamlessly with system-built modules.
4.2 Edit and Delete Features (Module CRUD)
Flowork provides full control over the modules you create:
* Editing a Module (✏️): If you hover over your custom module in the left sidebar, you will see a pencil icon. Clicking it reopens the builder modal, allowing you to patch bugs in your code. These changes take effect immediately without needing a reload. * Deleting a Module (🗑️): The trash icon is used to destroy a module. Warning: Deleting a module from the sidebar means permanently removing its logic from your computer. If that module is currently on the canvas, it will turn into a *zombie* (cannot be executed).
4.3 Exporting and Sharing (Share Protocol)
What if you create a brilliant Custom Module (like an "AI Sentiment Analyzer") and want to share it with your team?
* Export (📤): When you press the *Export* button, Flowork doesn't just package the node positions on the canvas, it encapsulates the entire source code (Metadata, JSON Schema, and JS Logic) of your custom modules into a single `.json` file. * Share via URL (🔗): If you press the *Share* button, your custom code is encrypted (Base64) and fused into the URL. When your colleague clicks that URL, their browser decrypts the code and automatically registers (installs) your created module into their computer's sidebar. This is known as Decentralized Module Sharing.
---
CHAPTER 5: Advanced Module Examples (The Library)
To truly master the Virtual App Studio, examining various logic patterns is essential. Here are 6 advanced node examples demonstrating data manipulation, API fetching, condition branching, and payload mapping.
5.1 Project: Async API Fetcher (Crypto Price)
This module demonstrates how to perform HTTP requests (fetch) securely from within the Node Sandbox.
App Name: Crypto Fetcher
Module Icon: 💰
Parameters Schema:
```json [ { "name": "coinSymbol", "displayName": "Coin Symbol (e.g. BTC, ETH)", "type": "string", "default": "BTC" } ]
```
Execution Logic:
```javascript const coin = (node.data.config.coinSymbol || "BTC").toUpperCase();
// Using CryptoCompare API because it doesn't block Client-Side (CORS) requests const url = `https://min-api.cryptocompare.com/data/price?fsym=${coin}&tsyms=USD`;
// Using await because it runs in an Asynchronous environment const response = await fetch(url);
if (!response.ok) { throw new Error(`Failed to fetch data. HTTP Status: ${response.status}`); }
const data = await response.json();
if (data.Response === "Error") { throw new Error(`API Error: ${data.Message}`); }
return { coin_requested: coin, price_usd: data.USD, timestamp: new Date().toISOString() };
```
5.2 Project: JSON Object Extractor (Data Parsing) - [REVISED]
Oftentimes, the previous node outputs a massive block of JSON data. This node helps you extract only the specific key you need. To make it testable as a standalone node, we added a fallback dummy data.
App Name: Key Extractor Module Icon: 🗜️ Parameters Schema: ```json [ { "name": "targetKey", "displayName": "JSON Key to Extract (e.g. data.user.id)", "type": "string", "default": "message" } ]
```
Execution Logic:
```javascript // FALLBACK MECHANISM: // If there is no previous node connected, use this dummy data for testing let payload = inputData;
if (!payload) { payload = { message: "Hello from Flowork OS!", data: { user: { id: 99, name: "Admin Neural" }, status: "active" } }; }
const keyPath = node.data.config.targetKey || "message";
// Function to safely access nested object properties using string path const getNestedValue = (obj, path) => { if (!path) return obj; return path.split('.').reduce((acc, part) => acc && acc[part], obj); };
const extractedValue = getNestedValue(payload, keyPath);
if (extractedValue === undefined) { throw new Error(`Key '${keyPath}' not found in the incoming payload.`); }
return { extracted_key: keyPath, value: extractedValue, original_payload: payload };
```
---
5.7 Project: Dynamic Date & Time Generator (Utility Node)
A common requirement in automation is generating the exact current time to stamp a database entry or append to a file name.
App Name: Time Stamp
Module Icon: 🕒
Parameters Schema:
```json [ { "name": "format", "displayName": "Output Format", "type": "options", "options": ["ISO String", "Date Only", "Time Only", "Unix Timestamp"], "default": "ISO String" } ]
```
Execution Logic:
```javascript const format = node.data.config.format || "ISO String"; const now = new Date();
let outputTime = "";
if (format === "ISO String") { outputTime = now.toISOString(); } else if (format === "Date Only") { outputTime = now.toLocaleDateString(); } else if (format === "Time Only") { outputTime = now.toLocaleTimeString(); } else if (format === "Unix Timestamp") { outputTime = now.getTime(); }
return { status: "success", requested_format: format, timestamp: outputTime, passed_data: inputData || null // Forwarding previous data if exists };
```
5.8 Project: Text Case Converter (String Transformation)
Often, data fetched from an API is messy. This node ensures your text is formatted cleanly (UPPERCASE, lowercase, or Capitalized) before it is sent to a database or message bot.
App Name: Text Formatter
Module Icon: 🔠
Parameters Schema:
```json [ { "name": "textToConvert", "displayName": "Text (Leave empty to use inputData.text)", "type": "string", "default": "" }, { "name": "caseType", "displayName": "Target Format", "type": "options", "options": ["UPPERCASE", "lowercase", "Capitalize"], "default": "UPPERCASE" } ]
```
Execution Logic:
```javascript // Priority: Use the UI Input. If empty, try to find 'text' from previous node. let rawText = node.data.config.textToConvert;
if (!rawText && inputData && inputData.text) { rawText = inputData.text; }
if (!rawText) { throw new Error("No text provided. Please fill the input or connect a node sending { text: '...' }"); }
const caseType = node.data.config.caseType || "UPPERCASE"; let convertedText = "";
if (caseType === "UPPERCASE") { convertedText = String(rawText).toUpperCase(); } else if (caseType === "lowercase") { convertedText = String(rawText).toLowerCase(); } else if (caseType === "Capitalize") { convertedText = String(rawText).replace(/\b\w/g, char => char.toUpperCase()); }
return { original_text: rawText, converted_text: convertedText, format_applied: caseType };
```
5.9 Project: Simple Math Evaluator (Computation Node)
Instead of building a calculator App, this is a calculator Node. It takes a mathematical expression and evaluates it safely in the pipeline.
App Name: Math Evaluator
Module Icon: 🧮
Parameters Schema:
```json [ { "name": "expression", "displayName": "Math Expression (e.g. 10 * 5)", "type": "string", "default": "10 * 5" } ]
```
Execution Logic:
```javascript const expression = node.data.config.expression || "0";
try { // Safe evaluation using Function constructor (avoids evil eval) const result = new Function('return ' + expression)(); return { expression: expression, result: result, is_integer: Number.isInteger(result), forwarded_payload: inputData || null }; } catch (error) { throw new Error(`Invalid mathematical expression: ${expression}`); }
```
5.10 Project: Data Validator & Blocker (Gatekeeper Node)
This node acts as a strict security guard. It checks if the data coming from the previous node contains a specific key. If it doesn't, it blocks the flow and throws an error, preventing broken data from being sent.
App Name: Strict Validator
Module Icon: 🛑
Parameters Schema:
```json [ { "name": "requiredKey", "displayName": "Required JSON Key (e.g. email)", "type": "string", "default": "email" } ]
```
Execution Logic:
```javascript if (!inputData) { throw new Error("Validation Failed: No incoming data detected."); }
const keyToCheck = node.data.config.requiredKey;
if (!keyToCheck) { return { status: "Skipped", message: "No validation key configured." }; }
// Check if the key exists in the inputData object if (!(keyToCheck in inputData)) { throw new Error(`Validation Failed: The key '${keyToCheck}' is missing from the payload!`); }
return { status: "Validation Passed", verified_key: keyToCheck, value: inputData[keyToCheck], verified_payload: inputData // Pass it to the next node securely };
```
5.11 Project: Random Password Node (Security Utility)
Generates a secure string. Very useful if you are building an automation flow that automatically registers users and needs to generate passwords on the fly.
App Name: Password Gen
Module Icon: 🔐
Parameters Schema:
```json [ { "name": "length", "displayName": "Password Length", "type": "string", "default": "16" }, { "name": "useSymbols", "displayName": "Include Symbols?", "type": "options", "options": ["Yes", "No"], "default": "Yes" } ]
```
Execution Logic:
```javascript const length = parseInt(node.data.config.length || "16"); const useSymbols = node.data.config.useSymbols === "Yes";
const alphaNumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const symbols = "!@#$%^&*()_+~`|}{[]:;?><,./-=";
const characterSet = useSymbols ? alphaNumeric + symbols : alphaNumeric;
let generatedPassword = ""; for (let i = 0; i < length; i++) { const randomIndex = Math.floor(Math.random() * characterSet.length); generatedPassword += characterSet[randomIndex]; }
return { password: generatedPassword, length: length, symbols_included: useSymbols };
```
5.12 Project: Mock Webhook Generator (Testing Trigger)
When you are designing a complex workflow, waiting for a real webhook to hit your server is annoying. This node generates a fake (mock) webhook payload instantly so you can test your subsequent nodes.
App Name: Mock Trigger
Module Icon: 📡
Parameters Schema:
```json [ { "name": "eventType", "displayName": "Simulate Event Type", "type": "options", "options": ["User Signup", "Payment Received", "Form Submitted"], "default": "User Signup" } ]
```
Execution Logic:
```javascript const eventType = node.data.config.eventType || "User Signup"; let mockPayload = {};
if (eventType === "User Signup") { mockPayload = { event: "user_created", user: { id: 1042, name: "John Doe", email: "john@floworkos.com" } }; } else if (eventType === "Payment Received") { mockPayload = { event: "payment_success", amount: 99.99, currency: "USD", transaction_id: "TXN_9982348" }; } else if (eventType === "Form Submitted") { mockPayload = { event: "form_entry", fields: { feedback: "This OS is amazing!", rating: 5 } }; }
return { trigger_source: "Mock Webhook Node", timestamp: new Date().toISOString(), payload: mockPayload };
```
---
Conclusion
With the Custom Module (UDF) feature, Flowork Neural Builder surpasses the traditional limits of an automation workflow. You don't have to wait for our developers to release a new module for the APIs or software you use. You are the architect. You can integrate internal company scripts, perform complex JSON data transformations, and even call external AI models—all written and executed securely and lightning-fast within the Flowork OS ecosystem.
Happy creating with limitless logic!
```
```