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:
- UI Definition File (
.hjson) - Defines how your extension appears in Wappler
- Implementation File (
.js) - Contains the actual server-side logic
- 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
DO:
- Match file names exactly -
mymodule.js and mymodule.hjson
- Use lowercase for module and action -
module: 'mymodule', action: 'render'
- Place files in correct directories:
.hjson → extensions/server_connect/modules/
.js → lib/modules/
- Use
this.parseOptional() in JavaScript functions
- Include
actionName and actionOutput in all modules
- Fully restart Wappler after changes
- Use HJSON format (not JSON) for UI definition
- Export functions with
exports.functionName
DON'T:
- Don't use uppercase in module/action names (causes "Module doesn't exist" error)
- Don't put files in subfolders - they must be directly in
modules/
- Don't use JSON format - must use HJSON (allows comments, no quotes needed)
- Don't use
async unless truly needed (keep functions synchronous when possible)
- Don't forget to install dependencies with npm
- Don't just reload project - must fully restart Wappler
- Don't use old
require('dmxAppConnect/lib/parser') - use this.parseOptional()
Testing Your Extension
1. Verify Installation
After restarting Wappler:
- Open a Server Connect API
- Click "+" to add a step
- Search for your extension name
- 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
- 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
-
Add installation instructions to README:
# Copy UI definition
cp yourmodule.hjson [PROJECT]/extensions/server_connect/modules/
# Copy implementation
cp yourmodule.js [PROJECT]/lib/modules/
-
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.
- 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
- 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
- Publish to NPM:
npm login
npm publish --access=public
- Users install via Wappler:
- Project Settings → Extensions
- Click "+" and enter:
wappler-your-extension
Additional Resources
Tips for Success
- Start Simple - Create a basic "Hello World" action first, then add complexity
- Test Incrementally - Test after each change to isolate issues quickly
- Study Existing Extensions - Look at published extensions on GitHub for patterns
- Use Version Control - Track changes to both
.hjson and .js files
- Document Your Extension - Write clear README with examples and installation steps
- 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' .