# Formatting

## Introduction

Webhooked uses Go templates enhanced with 100+ functions from [go-sprout](https://github.com/go-sprout/sprout) to transform webhook data. This powerful templating system allows you to reshape, validate, and enrich data before storage or in responses.

{% hint style="success" %}
You can validate your template by using the command `webhooked --validate --config webhooked.yaml`
{% endhint %}

## Template Functions

Webhooked use internally [go-template](https://pkg.go.dev/text/template) for functionality and  [sprout](https://github.com/go-sprout/sprout) to provide 100+ functions.

Full documentation of sprout are available here : [Sprout](https://docs.atom.codes/sprout/)&#x20;

## Template String

For simple template or a desire to have all configuration inside the yaml file, you can put your tempalte directly inline

```yaml
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

```yaml
formatting:
  templatePath: /config/templates/webhook-transform.tmpl
```

Template file (`webhook-transform.tmpl`):

```go
{{- /* 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

1. **Always parse JSON once** and reuse the result
2. **Use `with` blocks** for nil-safety
3. **Provide defaults** for optional fields
4. **Use external files** for complex templates
5. **Cache computed values** in variables
6. **Document complex logic** with comments
7. **Test templates** with various payloads

## Performance Optimization

#### Parse Once, use multiple times

:white\_check\_mark: <mark style="color:$success;">**GOOD**</mark>: Parse one

```yaml
formatting:
  templateString: |
    {{ with $data := fromJSON .Payload }}
      ID: {{ $data.id }}
      Type: {{ $data.type }}
      User: {{ $data.user_id }}
    {{ end }}
```

:x: <mark style="color:red;">**BAD**</mark>: Parse multiple times

```yaml
formatting:
  templateString: |
    ID: {{ (fromJSON .Payload).id }}
    Type: {{ (fromJSON .Payload).type }}
    User: {{ (fromJSON .Payload).user_id }}
```

#### Avoid Unnecessary Operations

:white\_check\_mark: <mark style="color:$success;">**GOOD**</mark>: Cache computed values

```yaml
formatting:
  templateString: |
    {{ $timestamp := now }}
    {{ $id := uuidv4 }}
    {{ with $data := fromJSON .Payload }}
      {
        "id": "{{ $id }}",
        "created": "{{ $timestamp }}",
        "modified": "{{ $timestamp }}",
        "reference": "{{ $id }}-{{ $timestamp | unixEpoch }}"
      }
    {{ end }}
```

:x: <mark style="color:red;">**BAD**</mark>: Recall functions multiples times can cause bug and latency

```yaml
formatting:
  templateString: |
    {{ with $data := fromJSON .Payload }}
      {
        "id": "{{ uuidv4 }}",
        "created": "{{ now }}",
        "modified": "{{ now }}",
        "reference": "{{ uuidv4 }}-{{ now | unixEpoch }}"
      }
    {{ end }}
```

#### Avoid String building

:white\_check\_mark: <mark style="color:$success;">**GOOD**</mark>: Use template engine

```yaml
formatting:
  templateString: |
    {{ with $data := fromJSON .Payload }}
      {{ $data.type }}:{{ $data.id }}:{{ $data.timestamp }}
    {{ end }}
```

:x: <mark style="color:red;">**BAD**</mark>: use go functions to build end strings

```yaml
formatting:
  templateString: |
    {{ with $data := fromJSON .Payload }}
      {{ printf "%s:%s:%d" $data.type $data.id $data.timestamp }}
    {{ end }}
```

{% hint style="info" %}
This is provided as example, `printf` function aren't available on sprout project
{% endhint %}

## Common Issues and Solutions

### Issue: Template Syntax Errors

```yaml
# Error: unexpected "}" in operand
formatting:
  templateString: '{{ .Value }'  # Missing closing brace

# Fixed:
formatting:
  templateString: '{{ .Value }}'
```

### Issue: JSON Escaping

```yaml
# Problem: Invalid JSON output
formatting:
  templateString: '{"message": "{{ .Message }}"}'

# Solution: Use toJSON for proper escaping
formatting:
  templateString: '{"message": {{ .Message | toJSON }}}'
```

### Issue: Nil Pointer

```yaml
# 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" }}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.atom.codes/webhooked/configuration/formatting.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
