Our prompt for creating Wappler Extensions with Claude

Thought this prompt may be useful to those wishing to create their own Wappler Extensions.

You'll need to replace 'MrCheese' with your own name as I don't want to take all the credit...

:innocent:

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

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
  9. Use this.getDbConnection(connectionName) to get a knex DB instance

: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()
  8. Don't require('./dbconnector') and call .getConnection() - use this.getDbConnection() instead

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', '');

5. Database Access

// ❌ WRONG - Do NOT require dbconnector and call getConnection()
const db = require('./dbconnector');
const knex = db.getConnection(connection); // This method does not exist

// ✅ CORRECT - Use this.getDbConnection() which is provided by the Wappler context
const knex = this.getDbConnection(connection);
if (!knex) throw new Error(`Connection "${connection}" doesn't exist.`);

This is critical. The this context in extension functions provides getDbConnection(name) which returns a knex instance for the named connection defined in your Wappler project. Never require('./dbconnector') and try to call methods on it directly — the module does not export getConnection.

Example of a complete DB-using async extension action:

exports.myAction = async function(options) {
    const connection = this.parseOptional(options.connection, 'string', '');
    const knex = this.getDbConnection(connection);

    if (!knex) throw new Error(`Connection "${connection}" doesn't exist.`);

    const rows = await knex('myTable')
        .select('id', 'name')
        .where('active', '1')
        .orderByRaw('RAND()')
        .limit(10);

    return { success: true, rows };
};

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!

8 Likes