Custom Module Examples
Custom Module Examples
Example 1: Data Transformation Module
A simple module with no external dependencies that reformats phone numbers.
package layers
import ( "fmt" "regexp" "strings")
type ModulePhoneFormatter struct { *PluginModule}
func (p *PluginModule) NewModulePhoneFormatter() *ModulePhoneFormatter { return &ModulePhoneFormatter{PluginModule: p}}
func (m *ModulePhoneFormatter) Type() string { return "phone-formatter" }func (m *ModulePhoneFormatter) RequiredComponents() []string { return []string{} }
func (m *ModulePhoneFormatter) ArgsExample() map[string]any { return map[string]any{ "phone": "+1 (555) 123-4567", "format": "e164", "countryCode": "1", }}
func (m *ModulePhoneFormatter) OutExample() map[string]any { return map[string]any{ "formatted": "+15551234567", "valid": true, "national": "(555) 123-4567", }}
func (m *ModulePhoneFormatter) Run(params Params) Response { input, ok := params.Args["phone"].(string) if !ok || input == "" { return params.Response().Fail("phone is required") }
format, _ := params.Args["format"].(string) countryCode, _ := params.Args["countryCode"].(string) if countryCode == "" { countryCode = "1" }
// Strip non-numeric characters digits := regexp.MustCompile(`\D`).ReplaceAllString(input, "")
if len(digits) < 10 { return params.Response().Done(map[string]any{ "formatted": input, "valid": false, "national": input, }) }
// Take last 10 digits for national number national := digits[len(digits)-10:] e164 := fmt.Sprintf("+%s%s", countryCode, national) nationalFormatted := fmt.Sprintf("(%s) %s-%s", national[:3], national[3:6], national[6:])
formatted := e164 if format == "national" { formatted = nationalFormatted } else if format == "digits" { formatted = strings.ReplaceAll(national, " ", "") }
params.Log(fmt.Sprintf("Formatted phone: %s → %s", input, formatted))
return params.Response().Done(map[string]any{ "formatted": formatted, "e164": e164, "national": nationalFormatted, "valid": true, })}Usage in flow:
{"appId": "phone-formatter", "ref": "fmt", "args": {"phone": "{{ $prev.getContact.phone }}", "format": "e164"}}Example 2: HTTP Integration Module
A module that calls an external API using the request component.
package layers
import ( "encoding/json" "fmt" "net/http")
type ModuleCurrencyConverter struct { *PluginModule}
func (p *PluginModule) NewModuleCurrencyConverter() *ModuleCurrencyConverter { return &ModuleCurrencyConverter{PluginModule: p}}
func (m *ModuleCurrencyConverter) Type() string { return "currency-converter" }func (m *ModuleCurrencyConverter) RequiredComponents() []string { return []string{"request"} }
func (m *ModuleCurrencyConverter) ArgsExample() map[string]any { return map[string]any{"amount": 100.0, "from": "USD", "to": "EUR", "apiKey": "your-key"}}
func (m *ModuleCurrencyConverter) OutExample() map[string]any { return map[string]any{"converted": 92.0, "rate": 0.92, "from": "USD", "to": "EUR"}}
func (m *ModuleCurrencyConverter) Run(params Params) Response { amount, ok := params.Args["amount"].(float64) if !ok { return params.Response().Fail("amount must be a number") } from, _ := params.Args["from"].(string) to, _ := params.Args["to"].(string) apiKey, _ := params.Args["apiKey"].(string)
reqComp, err := params.GetComponent("request") if err != nil { return params.Response().Fail("request component unavailable") }
url := fmt.Sprintf("https://api.exchangerate.host/convert?from=%s&to=%s&amount=%f&access_key=%s", from, to, amount, apiKey)
req, _ := http.NewRequest("GET", url, nil) resp, err := reqComp.Do(req) if err != nil { return params.Response().Fail("exchange rate API call failed: " + err.Error()) } defer resp.Body.Close()
var result struct { Result float64 `json:"result"` Info struct { Rate float64 `json:"rate"` } `json:"info"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return params.Response().Fail("failed to parse exchange rate response") }
return params.Response().Done(map[string]any{ "converted": result.Result, "rate": result.Info.Rate, "from": from, "to": to, "original": amount, })}Example 3: AI-Powered Sentiment Analysis Module
package layers
import "fmt"
type ModuleSentimentAnalyzer struct { *PluginModule}
func (p *PluginModule) NewModuleSentimentAnalyzer() *ModuleSentimentAnalyzer { return &ModuleSentimentAnalyzer{PluginModule: p}}
func (m *ModuleSentimentAnalyzer) Type() string { return "sentiment-analyzer" }func (m *ModuleSentimentAnalyzer) RequiredComponents() []string { return []string{"ai"} }
func (m *ModuleSentimentAnalyzer) ArgsExample() map[string]any { return map[string]any{"text": "The service was excellent!", "model": "gpt-4o-mini"}}
func (m *ModuleSentimentAnalyzer) OutExample() map[string]any { return map[string]any{"sentiment": "positive", "score": 0.95, "summary": "Very positive feedback"}}
func (m *ModuleSentimentAnalyzer) Run(params Params) Response { text, ok := params.Args["text"].(string) if !ok || text == "" { return params.Response().Fail("text is required") } model, _ := params.Args["model"].(string) if model == "" { model = "gpt-4o-mini" }
aiComp, err := params.GetComponent("ai") if err != nil { return params.Response().Fail("ai component unavailable") }
prompt := fmt.Sprintf(`Analyze the sentiment of this text and respond with JSON only:{"sentiment":"positive|negative|neutral","score":0.0-1.0,"summary":"brief summary"}
Text: %s`, text)
result, err := aiComp.Chat([]map[string]string{ {"role": "system", "content": "You are a sentiment analysis expert. Always respond with valid JSON."}, {"role": "user", "content": prompt}, }, map[string]any{"model": model, "temperature": 0.1})
if err != nil { return params.Response().Fail("AI call failed: " + err.Error()) }
params.Log(fmt.Sprintf("Sentiment analysis complete for %d chars", len(text)))
return params.Response().Done(map[string]any{ "raw": result, "text": text, "model": model, })}