File upload with encryption using public key client side (App Connect extension)

Hope this helps for those wishing to create NodeJS Extensions. Here is our extension.md file which aids LLMs in creating extensions.


How to Create a Wappler Server Connect Extension

A comprehensive guide for creating custom Server Connect extensions for Wappler, documenting best practices and correct setup procedures.

Table of Contents

  • Overview
  • Prerequisites
  • File Structure
  • Step-by-Step Guide
  • Critical Setup Requirements
  • Testing Your Extension
  • Common Pitfalls
  • Publishing Your Extension

Overview

Wappler Server Connect extensions allow you to create custom server actions that integrate seamlessly into the Wappler IDE. Extensions consist of:

  1. UI Definition File (.hjson) - Defines how your extension appears in Wappler
  2. Implementation File (.js) - Contains the actual server-side logic
  3. Dependencies (optional) - Any npm packages your extension needs

Prerequisites

  • Wappler 6.0.0 or higher
  • Node.js 14.0.0 or higher
  • Understanding of JavaScript and Node.js
  • Familiarity with Wappler Server Connect

File Structure

Distribution Structure (for GitHub/NPM)

When you distribute your extension, use a flat structure:

your-extension/
├── README.md
├── package.json
├── yourmodule.hjson        ← UI definition
├── yourmodule.js           ← Implementation code
└── .gitignore              ← Optional, only if using Git

Note: .gitignore is only needed if you're using Git for version control. .npmignore is not needed for manual distribution.

Installation Structure (in Wappler Projects)

When users install your extension, files go to specific locations:

[Your-Wappler-Project]/
├── extensions/
│   └── server_connect/
│       └── modules/
│           └── yourmodule.hjson        ← Copy UI definition here
└── lib/
    └── modules/
        └── yourmodule.js               ← Copy implementation here

CRITICAL:

  • File names MUST match (case-sensitive)
  • For distribution: keep files at root level
  • For installation: .hjson goes in extensions/server_connect/modules/
  • For installation: .js goes in lib/modules/
  • Both files should have the same base name

Step-by-Step Guide

Step 1: Create the Implementation File (.js)

Create lib/modules/yourmodule.js:

// Example: lib/modules/myextension.js

// Import any dependencies
const someLib = require('some-library');

// Export your action functions
exports.yourAction = function(options) {
    // Access parsed options using this.parseOptional()
    const param1 = this.parseOptional(options.param1, 'string', '');
    const param2 = this.parseOptional(options.param2, 'object', {});
    
    try {
        // Your logic here
        const result = doSomething(param1, param2);
        
        // Return your results
        return {
            success: true,
            data: result
        };
    } catch (error) {
        return {
            success: false,
            error: error.message
        };
    }
};

// You can export multiple actions
exports.anotherAction = function(options) {
    // Another action implementation
};

Key Points:

  • Use exports.functionName for each action
  • Function name MUST be lowercase (e.g., render, not Render)
  • Use this.parseOptional() to parse options
  • Return an object with your results
  • Handle errors gracefully

Important: Output Checkbox

  • For the actionOutput field, use initValue: true, defaultValue: false
  • initValue = initial checkbox state when action is added
  • defaultValue = value that causes the property to be omitted from JSON
  • This ensures the Output checkbox works correctly

Step 2: Create the UI Definition File (.hjson)

Create extensions/server_connect/modules/yourmodule.hjson:

{
  type: 'yourmodule_action'           # Unique identifier
  module: 'yourmodule'                # MUST match .js filename (lowercase)
  action: 'yourAction'                # MUST match exported function name
  groupTitle: 'Mr Cheese'             # Group name in Wappler UI
  groupIcon: 'fas fa-lg fa-cheese comp-exec'
  title: 'Your Action'                # Action display name
  icon: 'fas fa-lg fa-cog comp-exec'
  
  # Define output data structure
  dataScheme: [
    {name: 'success', type: 'boolean'}
    {name: 'data', type: 'text'}
    {name: 'error', type: 'text'}
  ]
  
  # Make the action pickable in data bindings
  dataPickObject: true
  
  # Define input properties
  properties: [
    {
      group: 'Settings'
      variables: [
        {
          name: 'actionName'
          optionName: 'name'
          title: 'Name'
          type: 'text'
          required: true
          defaultValue: ''
          baseName: 'myaction'
          help: 'The name for this action step'
        }
        {
          name: 'actionOutput'
          optionName: 'output'
          title: 'Output'
          type: 'boolean'
          initValue: true
          defaultValue: false
          help: 'Output the results'
        }
        {
          name: 'param1'
          optionName: 'param1'
          title: 'Parameter 1'
          type: 'text'
          serverDataBindings: true
          defaultValue: ''
          help: 'Description of parameter 1'
        }
        {
          name: 'param2'
          optionName: 'param2'
          title: 'Parameter 2'
          type: 'file'              # Use 'file' for file picker
          serverDataBindings: true
          defaultValue: ''
          help: 'Select a file'
        }
      ]
    }
  ]
  
  # Auto-install npm dependencies
  usedModules: {
    node: {
      "some-library": "^1.0.0"
    }
  }
}

Key HJSON Properties:

Property Required Description
type Yes Unique identifier for the action
module Yes MUST match .js filename (lowercase)
action Yes MUST match exported function name
groupTitle Yes Always use 'Mr Cheese' for all extensions
groupIcon Yes FontAwesome icon (use fa-cheese for consistency)
title Yes Display name for the action
icon Yes FontAwesome icon for the action
dataScheme Optional Output data structure
dataPickObject Optional Make action pickable in bindings
properties Yes Array of input property groups
usedModules Optional Auto-install dependencies

Step 3: Input Field Types

Common input field types for the type property:

Type Description Example
text Single line text input type: 'text'
textarea Multi-line text input type: 'textarea'
boolean Checkbox type: 'boolean'
number Number input (static only) type: 'number'
numberorstring Dynamic number/string type: 'numberorstring'
file File picker dialog type: 'file'
folder Folder picker dialog type: 'folder'
droplist Dropdown select See advanced example

Advanced: Dropdown List

{
  name: 'format'
  optionName: 'format'
  title: 'Output Format'
  type: 'droplist'
  values: [
    {title: 'JSON', value: 'json'}
    {title: 'XML', value: 'xml'}
    {title: 'CSV', value: 'csv'}
  ]
  defaultValue: 'json'
}

Step 4: Icon Color Modifiers

Use these color classes with FontAwesome icons:

Class Color Usage
comp-flows Blue Flow control
comp-errors Red Error handling
comp-data Teal Data operations
comp-loops Purple Loop actions
comp-general Gray General actions
comp-settings Orange Settings
comp-exec Green Execution
comp-files Yellow File operations

Example: fas fa-lg fa-database comp-data

Step 5: Install Dependencies

In your project root:

npm install your-dependencies

Or let Wappler auto-install by defining in usedModules:

usedModules: {
  node: {
    "ejs": "^3.1.9"
    "axios": "^1.6.0"
  }
}

Step 6: Restart Wappler

IMPORTANT: You MUST completely quit and restart Wappler after:

  • Adding new extensions
  • Modifying .hjson files
  • Installing dependencies

Simply reloading the project is NOT enough!

Critical Setup Requirements

:white_check_mark: DO:

  1. Match file names exactly - mymodule.js and mymodule.hjson
  2. Use lowercase for module and action - module: 'mymodule', action: 'render'
  3. Place files in correct directories:
    • .hjsonextensions/server_connect/modules/
    • .jslib/modules/
  4. Use this.parseOptional() in JavaScript functions
  5. Include actionName and actionOutput in all modules
  6. Fully restart Wappler after changes
  7. Use HJSON format (not JSON) for UI definition
  8. Export functions with exports.functionName

:cross_mark: DON'T:

  1. Don't use uppercase in module/action names (causes "Module doesn't exist" error)
  2. Don't put files in subfolders - they must be directly in modules/
  3. Don't use JSON format - must use HJSON (allows comments, no quotes needed)
  4. Don't use async unless truly needed (keep functions synchronous when possible)
  5. Don't forget to install dependencies with npm
  6. Don't just reload project - must fully restart Wappler
  7. Don't use old require('dmxAppConnect/lib/parser') - use this.parseOptional()

Testing Your Extension

1. Verify Installation

After restarting Wappler:

  1. Open a Server Connect API
  2. Click "+" to add a step
  3. Search for your extension name
  4. It should appear in the list

2. Test the Action

Create a test API:

1. Your Extension Action
   - Configure parameters
   - Set Output: true

2. Test in browser
   - Visit: /api/your-test.json
   - Check the JSON response

3. Debug Errors

Common errors and solutions:

"Module XXX doesn't exist"

  • Check: module name in .hjson matches .js filename (case-sensitive)
  • Check: .js file is in lib/modules/
  • Check: function is exported correctly

Extension doesn't appear in Wappler

  • Check: .hjson file is in extensions/server_connect/modules/
  • Check: HJSON syntax is valid (no JSON syntax errors)
  • Check: Wappler was fully restarted

"Cannot find module"

  • Check: Dependencies installed with npm install
  • Check: usedModules defined in .hjson

Common Pitfalls

1. Case Sensitivity Issues

# ❌ WRONG
module: 'SSR'
action: 'Render'

# ✅ CORRECT
module: 'ssr'
action: 'render'

2. File Location Errors

# ❌ WRONG
extensions/server_connect/modules/ssr/ssr.hjson
extensions/server_connect/modules/ssr/ssr.js

# ✅ CORRECT
extensions/server_connect/modules/ssr.hjson
lib/modules/ssr.js

3. JSON vs HJSON

// ❌ WRONG - JSON format
{
  "type": "myaction",
  "module": "mymodule"
}

// ✅ CORRECT - HJSON format
{
  type: 'myaction'
  module: 'mymodule'
}

4. Parser Usage

// ❌ WRONG - Old method
const parse = require('dmxAppConnect/lib/parser');
const value = parse.parseOptional(this, options.param, 'string', '');

// ✅ CORRECT - New method
const value = this.parseOptional(options.param, 'string', '');

Publishing Your Extension

Option 1: GitHub Repository

  1. Create a repository with flat structure:
your-extension/
├── README.md
├── package.json
├── yourmodule.hjson        ← UI definition at root
├── yourmodule.js           ← Implementation at root
├── .gitignore              ← Optional: node_modules/, *.log, .DS_Store
└── examples/               ← Optional: usage examples
  1. Add installation instructions to README:

    # Copy UI definition
    cp yourmodule.hjson [PROJECT]/extensions/server_connect/modules/
    
    # Copy implementation
    cp yourmodule.js [PROJECT]/lib/modules/
    
  2. Users clone repository and copy files to their Wappler project directories

Option 2: NPM Package

Note: For NPM packages that Wappler can auto-install, you may need nested directories. However, for manual distribution, use the flat structure from Option 1.

  1. Create package structure (if supporting auto-install):
your-wappler-extension/
├── package.json
├── server_connect/
│   └── modules/
│       └── yourmodule.hjson
└── lib/
    └── modules/
        └── yourmodule.js

OR use flat structure for manual installation:

your-wappler-extension/
├── package.json
├── yourmodule.hjson
└── yourmodule.js
  1. Update package.json:

For flat structure (recommended):

{
  "name": "wappler-your-extension",
  "version": "1.0.0",
  "description": "Your extension description",
  "main": "yourmodule.js",
  "keywords": ["wappler", "server-connect", "extension"],
  "author": "MrCheese",
  "license": "MIT",
  "wappler": {
    "module": "yourmodule",
    "version": "1.0.0"
  }
}

For nested structure:

{
  "name": "wappler-your-extension",
  "version": "1.0.0",
  "description": "Your extension description",
  "main": "lib/modules/yourmodule.js",
  "keywords": ["wappler", "server-connect", "extension"],
  "author": "MrCheese",
  "license": "MIT",
  "wappler": {
    "module": "yourmodule",
    "version": "1.0.0"
  }
}

Important Notes:

  • The "main" field should match your actual file structure
  • Always use "author": "MrCheese" for all extensions
  • Do NOT include dependencies or engines fields - extensions run within Wappler projects
  • Use usedModules in the .hjson file instead to auto-install dependencies in user projects
  1. Publish to NPM:
npm login
npm publish --access=public
  1. Users install via Wappler:
    • Project Settings → Extensions
    • Click "+" and enter: wappler-your-extension

Additional Resources

Tips for Success

  1. Start Simple - Create a basic "Hello World" action first, then add complexity
  2. Test Incrementally - Test after each change to isolate issues quickly
  3. Study Existing Extensions - Look at published extensions on GitHub for patterns
  4. Use Version Control - Track changes to both .hjson and .js files
  5. Document Your Extension - Write clear README with examples and installation steps
  6. Handle Errors Gracefully - Always return error information to help users debug

Remember: The key to success is matching names exactly (case-sensitive), placing files in the correct directories, and fully restarting Wappler after any changes!


If you use this as direction please be sure to find and replace all instances of 'MrCheese' .

1 Like