Formatting
Templates are powerful. Use them wisely to transform your webhook data exactly as needed.
Introduction
Webhooked uses Go templates enhanced with 100+ functions from go-sprout to transform webhook data. This powerful templating system allows you to reshape, validate, and enrich data before storage or in responses.
You can validate your template by using the command webhooked --validate --config webhooked.yaml
Template Functions
Webhooked use internally go-template for functionality and sprout to provide 100+ functions.
Full documentation of sprout are available here : Sprout
Template String
For simple template or a desire to have all configuration inside the yaml file, you can put your tempalte directly inline
formatting:
templateString: |
{{- with $data := fromJSON .Payload -}}
{{- $timestamp := $data.created_at | toDate "2006-01-02T15:04:05Z07:00" | unixEpoch -}}
{
"id": "{{ $data.id }}",
"created": "{{ $timestamp }}",
"reference": "{{ $data.id }}-{{ $timestamp }}"
}
{{- end -}}Template Files
For complex templates, usage of external files are recommended
formatting:
templatePath: /config/templates/webhook-transform.tmplTemplate file (webhook-transform.tmpl):
{{- /* Complex webhook transformation template */ -}}
{{- $data := fromJSON .Payload -}}
{{- $config := dict
"environment" (env "ENVIRONMENT")
"version" (env "VERSION")
"region" (env "REGION")
-}}
{
"metadata": {{ $config | toJSON }},
"payload": {{ $data | toJSON }},
"processed": "{{ now }}"
}Best Practices
Always parse JSON once and reuse the result
Use
withblocks for nil-safetyProvide defaults for optional fields
Use external files for complex templates
Cache computed values in variables
Document complex logic with comments
Test templates with various payloads
Performance Optimization
Parse Once, use multiple times
✅ GOOD: Parse one
formatting:
templateString: |
{{ with $data := fromJSON .Payload }}
ID: {{ $data.id }}
Type: {{ $data.type }}
User: {{ $data.user_id }}
{{ end }}❌ BAD: Parse multiple times
formatting:
templateString: |
ID: {{ (fromJSON .Payload).id }}
Type: {{ (fromJSON .Payload).type }}
User: {{ (fromJSON .Payload).user_id }}Avoid Unnecessary Operations
✅ GOOD: Cache computed values
formatting:
templateString: |
{{ $timestamp := now }}
{{ $id := uuidv4 }}
{{ with $data := fromJSON .Payload }}
{
"id": "{{ $id }}",
"created": "{{ $timestamp }}",
"modified": "{{ $timestamp }}",
"reference": "{{ $id }}-{{ $timestamp | unixEpoch }}"
}
{{ end }}❌ BAD: Recall functions multiples times can cause bug and latency
formatting:
templateString: |
{{ with $data := fromJSON .Payload }}
{
"id": "{{ uuidv4 }}",
"created": "{{ now }}",
"modified": "{{ now }}",
"reference": "{{ uuidv4 }}-{{ now | unixEpoch }}"
}
{{ end }}Avoid String building
✅ GOOD: Use template engine
formatting:
templateString: |
{{ with $data := fromJSON .Payload }}
{{ $data.type }}:{{ $data.id }}:{{ $data.timestamp }}
{{ end }}❌ BAD: use go functions to build end strings
formatting:
templateString: |
{{ with $data := fromJSON .Payload }}
{{ printf "%s:%s:%d" $data.type $data.id $data.timestamp }}
{{ end }}Common Issues and Solutions
Issue: Template Syntax Errors
# Error: unexpected "}" in operand
formatting:
templateString: '{{ .Value }' # Missing closing brace
# Fixed:
formatting:
templateString: '{{ .Value }}'Issue: JSON Escaping
# Problem: Invalid JSON output
formatting:
templateString: '{"message": "{{ .Message }}"}'
# Solution: Use toJSON for proper escaping
formatting:
templateString: '{"message": {{ .Message | toJSON }}}'Issue: Nil Pointer
# Problem: Accessing non-existent field
formatting:
templateString: '{{ $data.user.uame }}'
# Solution: Safe navigation using dig and default
formatting:
templateString: |
{{ $data | dig "user.uame" | default "Unknown" }}Last updated