# Go Templates Guide
## Basic Syntax
### Delimiters
- `{{ }}` - standard evaluation
- `{{- }}` - trim whitespace left
- `{{- -}}` - trim whitespace both sides
- `{{ -}}` - trim whitespace right
### Variables and References
- `$x := value` - variable assignment
- `$` - root/global context
- `.` - current context
- `.Field` - field access
- `$.Field` - root context field access
- `$x.Field` - variable field access
### Pipeline and Functions
- `x | f` - pipe value to function
- `| default "val"` - set default value
- `| nindent N` - indent N spaces with newline
- `| toYaml` - convert to YAML
- `| quote` - quote string
### Control Structures
```go
{{if x}} {{else}} {{end}}
{{range x}} {{end}}
{{with x}} {{end}} // scope assignment
{{define "name"}} {{end}} // define template
{{template "name" .}} // include template
{{block "name" .}} {{end}} // define with fallback
```
## Built-in Functions
### Operators
- Comparison: `eq`, `ne`, `lt`, `gt`, `le`, `ge`
- Boolean: `and`, `or`, `not`
- Formatting: `print`, `printf`
- Collections: `len`, `index`
### Control Flow
```go
ternary $condition "yes" "no" // conditional value selection
coalesce $val1 $val2 $val3 // first non-empty value
required "msg" $value // fail if value empty
fail "error message" // explicit failure
```
### String Operations
```go
cat "hello" "world" // string concatenation
join "," $slice // join slice with separator
split "," $string // split string into slice
trim $string // remove whitespace
trimAll "$" $string // remove all occurrences
upper $string // uppercase
lower $string // lowercase
```
### Collection Operations
```go
hasKey $map "key" // check map key existence
keys $map // get map keys as slice
values $map // get map values as slice
compact $slice // remove empty values
uniq $slice // remove duplicates
reverse $slice // reverse order
shuffle $slice // randomize order
```
## Scoping and Context
### Scope Changes
```go
// With block - changes context
{{ with $x := .Values }}
{{ .property }} // . is now $x
{{ $.Release.Name }} // $ still refers to root
{{ end }}
// Range block - iteration context
{{ range $i, $v := .Values.list }}
{{ $v }} // current item
{{ $.Release.Name }} // root context
{{ end }}
// Define block - template scope
{{ define "my.template" }}
{{ . }} // context passed to template
{{ $ }} // root of template call
{{ end }}
```
### Template Inheritance
#### Define vs Block
```go
// Define - only for creating reusable templates
{{ define "base" }}
<h1>{{ .title }}</h1>
{{ template "content" . }} // content must be defined elsewhere
{{ end }}
// Block - provides default content that can be overridden
{{ block "content" . }}
Default content here
{{ end }}
// Child template overriding a block
{{ define "child" }}
{{ template "base" . }}
{{ define "content" }} // Override the content block
Custom child content
{{ end }}
{{ end }}
// Practical example with multiple blocks
{{ define "base" }}
<html>
<head>
{{ block "title" . }}Default Title{{ end }}
{{ block "meta" . }}
<meta charset="utf-8">
{{ end }}
</head>
<body>
{{ block "content" . }}
<p>Default content</p>
{{ end }}
{{ block "footer" . }}
<footer>Default footer</footer>
{{ end }}
</body>
</html>
{{ end }}
// Child template overriding specific blocks
{{ define "homepage" }}
{{ template "base" . }}
{{ define "title" }}Home - My Site{{ end }}
{{ define "content" }}
<h1>Welcome!</h1>
{{ end }}
{{ end }}
```
## Type System
### Basic Types
- `string`
- `bool`
- `int`, `float64`
- `nil`
### Collections
- `slice/array` - indexed sequences
- `map` - key/value store
- `struct` - dot-accessible fields
### Debugging Templates
#### Type Inspection
When debugging templates, understanding the types of values is crucial:
```go
// Get basic type information
kindOf $x // returns "string", "map", "slice", etc.
typeOf $x // returns detailed Go type
typeIs "string" $x // boolean type check
```
#### Value Inspection
Use `printf` with different format verbs to inspect values:
```go
// Debug printing with different detail levels
printf "%v" $x // basic value format
printf "%+v" $x // adds field names for structs
printf "%#v" $x // Go-syntax representation
printf "%T" $x // type information
// Debugging context changes
{{ range $i, $v := .Values.list }}
{{- printf "Index: %d, Value: %v, Type: %T" $i $v $v }}
{{- printf "Current context: %v" . }}
{{- printf "Root context: %v" $ }}
{{ end }}
```
#### Common Debugging Patterns
Here are some patterns for troubleshooting template issues:
```go
// Check if map key exists before accessing
{{ if hasKey .Values "mykey" }}
{{ printf "Found key with value: %v" .Values.mykey }}
{{ else }}
{{ printf "Key 'mykey' not found in Values" }}
{{ end }}
// Validate nested structure
{{ if and .Values.config (hasKey .Values.config "nested") }}
{{ printf "Nested config: %v" .Values.config.nested }}
{{ else }}
{{ fail "Missing required nested configuration" }}
{{ end }}
// Debug type mismatches
{{ $val := .Values.someValue }}
{{ if not (typeIs "string" $val) }}
{{ printf "Expected string, got %s (%T)" (kindOf $val) $val }}
{{ end }}
```
### Type Operations
```go
// Type information
kindOf $x // basic type category
typeOf $x // detailed Go type
typeIs "string" $x // type checking
// Debugging
printf "%#v" $x // detailed value format
printf "%T" $x // type information
printf "%v" $x // default format
// Zero/nil checking
if $x // false for zero values
if kindIs "invalid" $x // checks for nil
```
### Type Conversions
```go
// String conversions
toString 42 // "42"
print (toString .) // convert current context
// Numeric conversions
toInt "42" // 42
toInt64 "42" // 42 (int64)
toFloat64 "42.1" // 42.1
// Data structure conversions
toJson . // convert to JSON string
fromJson "..." // parse JSON to object
toYaml . // convert to YAML string
fromYaml "..." // parse YAML to object
// Bool conversion
toBool 1 // true
toBool "true" // true
toBool "yes" // true
```
## Advanced Examples
### Complex Pipeline Transformations
```go
{{ .Values.data |
split "," | // split into slice
compact | // remove empty
uniq | // remove duplicates
join "," | // rejoin
quote // add quotes
}}
### Handling Nested Data Structures
#### Iterating Over Nested Data
Working with deeply nested structures often requires careful handling of context and scopes. Here's how to traverse complex data safely:
```go
// Processing deeply nested maps/lists
{{ range $svcName, $svc := .Values.services }}
{{ range $envName, $env := $svc.environments }}
{{ range $cfgName, $cfg := $env.config }}
// Access with full path for clarity
{{ $value := index $.Values.services $svcName "environments" $envName "config" $cfgName }}
// Or use nested context
{{ $value := $cfg }}
{{ end }}
{{ end }}
{{ end }}
```
#### Accessing Values Dynamically
When you need to access nested values using dynamic paths (e.g., from configuration), you can split the path and traverse:
```go
// Dynamic access to nested JSON/YAML
{{ $path := split "services.frontend.port" "." }}
{{ $value := $.Values }}
{{ range $path }}
{{ $value = index $value . }}
{{ end }}
{{ $value }} // Final value
```
#### Building Nested Structures
Sometimes you need to construct nested structures from flat data. This pattern is useful when working with configuration management:
```go
// Building nested structures
{{ $config := dict }}
{{ range $path, $value := .Values.flat }}
{{ $parts := split "." $path }}
{{ $current := $config }}
{{ range $i, $part := $parts }}
{{ if eq (add $i 1) (len $parts) }}
{{ $_ := set $current $part $value }}
{{ else }}
{{ if not (hasKey $current $part) }}
{{ $_ := set $current $part dict }}
{{ end }}
{{ $current = index $current $part }}
{{ end }}
{{ end }}
{{ end }}
```
#### Merging Default Values
When working with configuration, you often need to merge default values with overrides while preserving nested structures:
```go
// Handling nested defaults
{{ define "merge" }}
{{ $default := index . 0 }}
{{ $override := index . 1 }}
{{ range $k, $v := $default }}
{{ if hasKey $override $k }}
{{ if kindIs "map" $v }}
{{ $newV := dict }}
{{ template "merge" (list $v (index $override $k) $newV) }}
{{ $_ := set $override $k $newV }}
{{ end }}
{{ else }}
{{ $_ := set $override $k $v }}
{{ end }}
{{ end }}
{{ end }}
```
```go
// Processing deeply nested maps/lists
{{ range $svcName, $svc := .Values.services }}
{{ range $envName, $env := $svc.environments }}
{{ range $cfgName, $cfg := $env.config }}
// Access with full path for clarity
{{ $value := index $.Values.services $svcName "environments" $envName "config" $cfgName }}
// Or use nested context
{{ $value := $cfg }}
{{ end }}
{{ end }}
{{ end }}
// Dynamic access to nested JSON/YAML
{{ $path := split "services.frontend.port" "." }}
{{ $value := $.Values }}
{{ range $path }}
{{ $value = index $value . }}
{{ end }}
{{ $value }} // Final value
// Building nested structures
{{ $config := dict }}
{{ range $path, $value := .Values.flat }}
{{ $parts := split "." $path }}
{{ $current := $config }}
{{ range $i, $part := $parts }}
{{ if eq (add $i 1) (len $parts) }}
{{ $_ := set $current $part $value }}
{{ else }}
{{ if not (hasKey $current $part) }}
{{ $_ := set $current $part dict }}
{{ end }}
{{ $current = index $current $part }}
{{ end }}
{{ end }}
{{ end }}
// Handling nested defaults
{{ define "merge" }}
{{ $default := index . 0 }}
{{ $override := index . 1 }}
{{ range $k, $v := $default }}
{{ if hasKey $override $k }}
{{ if kindIs "map" $v }}
{{ $newV := dict }}
{{ template "merge" (list $v (index $override $k) $newV) }}
{{ $_ := set $override $k $newV }}
{{ end }}
{{ else }}
{{ $_ := set $override $k $v }}
{{ end }}
{{ end }}
{{ end }}
```
```
### Error Handling Patterns
Error handling in Go templates provides two main mechanisms:
- `fail` - Immediately stops template execution with an error
- `required` - Checks for empty/nil values and fails with a custom message
Both can be used for validation, but `fail` gives more control over error conditions.
```go
// Basic required and fail usage
{{ required "database password is required" .Values.dbPassword }}
{{ if not .Values.apiKey }}
{{ fail "API key must be provided" }}
{{ end }}
// Combining fail with coalesce for fallback logic
{{ $endpoint := coalesce .Values.endpoint .Values.fallbackEndpoint | required "endpoint is required" }}
{{ $timeout := coalesce .Values.timeout "30s" }} // with default value
// Required inside nested range
{{ range .Values.services }}
{{ $name := required "service name is required" .name }}
{{ $port := required (printf "port is required for service %s" $name) .port }}
{{ end }}
// Complex validation with fail and coalesce
{{ $dbConfig := dict }}
{{ $dbConfig = set $dbConfig "host" (coalesce .Values.db.host "localhost") }}
{{ $dbConfig = set $dbConfig "port" (coalesce .Values.db.port 5432) }}
{{ $dbConfig = set $dbConfig "password" (required "database password is required" .Values.db.password) }}
{{ if not (hasKey $dbConfig "host") }}
{{ fail "database host configuration is incomplete" }}
{{ end }}
// Type-safe processing with detailed error
{{- if typeIs "string" $x -}}
{{- $num := toInt $x -}}
{{- printf "%d" $num -}}
{{- else -}}
{{- fail (printf "expected string value, got %s" (kindOf $x)) -}}
{{- end -}}
```
## Key Points
- No explicit type declarations
- Dynamic/loose typing
- Automatic type coercion in many cases
- No user-defined types
- No generics/parametric types
- Limited error handling for type mismatches
- Conversions return errors on failure
- No implicit type coercion in most cases
- Limited set of conversions available
- toYaml/fromYaml are Helm-specific
- Complex conversions often require multiple steps using pipes