Skip to content

Templating language

Page as Markdown

Learn how to define powerful staged transformations with the advanced templating engine.

Solo Enterprise for kgateway transformation templates are powered by v3.4 of the Inja template engine, which is inspired by the popular Jinja templating language in Python. The template lets you transform headers and body information of a request or response based on the header and body properties themselves.

The following YAML file shows the structure of the transformation template and all the attributes that you can configure. To learn more about each attribute, see Template attributes.


          transformation:
            template:
              advancedTemplate:
              bodyTransformation:                
              dynamicMetadataValues:
              escapeCharacters:
              extractors:
              headers: 
              headersToAppend: 
              headersToRemove:
              ignoreErrorOnParse:
              parseBodyBehavior:
              spanTransformer:

When writing your templates, you can take advantage of all the core Inja features, such as loops, conditional logic, and functions. In addition, Solo Enterprise for kgateway comes with custom Inja functions that you can use to transform request and response metadata more easily.

Custom Inja functions

When specifying your transformation template, you can leverage custom Solo Enterprise for kgateway functions that can help to extract and transform headers and bodies more easily. These functions are available in the headers, headersToAppend, bodyTransformation, and dynamicMetadataValues attributes of your transformation template.

FunctionDescription
base64_encode(string)Encodes the input string to base64.
base64_decode(string)Decodes the input string from base64. For an example, see the Decode base64 headers guide in simple transformations.
body()Returns the request/response body. For an example, see Update response body.
context()Returns the base JSON context. You can use this context to parse a JSON body that is an array.
dynamic_metadata("KEY", "NAMESPACE")Returns a value from Envoy dynamic metadata. If NAMESPACE is omitted, the default io.solo.transformation namespace is used, which is the namespace set by the dynamicMetadataValues attribute. Use colon-separated keys to access nested values (e.g. "parent:child"). Note that array values are returned as a flat string, so Inja for loops cannot iterate over them. For usage examples, see dynamic_metadata examples.
env(env_var_name)Returns the value of the environment variable with the given name. Note that because the transformation filter is processed in the gateway proxy, the environment variables are returned in the context of the gateway proxy. For an example, see Enrich access logs.
extraction(extractor_name)Returns the value of the extractor with the given name. This function is required to access extractors when advancedTemplates is set to true. For more information, see advancedTemplates.
header(header_name)Returns the value of the header with the given name. For an example, see the Change response status guide in simple transformations.
raw_string(string)Returns the input string with escaped characters intact. This function is useful for constructing JSON request or response bodies. For an example, see the Inject response headers guide in simple transformations.
replace_with_random(string, pattern)Finds the pattern in the input string and replaces this pattern with a random string. For an example, see Update response body.
request_header(header_name)Returns the value of the request header with the given name. This function is useful to add the request header values in response transformations. For an example for how to extract a request header value and inject it into a response header, see Append headers.
substring(string, start_pos, substring_len)Returns a substring of the input string, starting at start_pos and extending for substring_len characters. If no substring_len is provided or substring_len is <= 0, the substring extends to the end of the input string. For an example, see the Decode base64 headers guide in simple transformations.

Other common functions

You might use default Inja functions, such as if else or if exists. For an example, see the Change response status guide in simple transformations.

dynamic_metadata examples

The dynamic_metadata function reads values from Envoy dynamic metadata that other filters or earlier transformation stages have set.

Default namespace: Read a value that was set by dynamicMetadataValues in an earlier transformation stage. Because no namespace is specified, the default io.solo.transformation namespace is used.

{{ dynamic_metadata("my_key") }}

External auth claims: Read claims that were set by an external auth filter (such as dynamicMetadataFromClaims in an AuthConfig). Use the envoy.filters.http.ext_authz namespace and colon-separated keys to access nested values.

{{ dynamic_metadata("config_0:sub", "envoy.filters.http.ext_authz") }}

JWT authentication claims: Read claims from the JWT authentication filter.

{{ dynamic_metadata("provider:email", "envoy.filters.http.jwt_authn") }}

Template attributes

Learn more about the template attributes that you can use to transform headers and bodies of requests and responses.

advancedTemplate

This attribute determines which notation Solo Enterprise for kgateway uses when accessing elements in JSON structures. If set to true, Solo Enterprise for kgateway expects JSON pointer notation (time/start) instead of dot notation (time.start). The default value is false.


transformation:
  template:
    advancedTemplates: false

bodyTransformation

Apply transformation templates to request or response bodies. The bodyTransformation attribute allows you to specify the structure of the body that you want to return. For example, you can replace values in the body, extract values from headers and add them to the body, or return a static body.

To add a static value to the body, you can use the following transformation template.


transformation:
  template:  
    bodyTransformation: 
      type: Body
      body: 'This is my static body'

This template results in a This is my static body body.

The following example uses an Inja function to get the POD_NAME environment variable from the gateway proxy and returns that value in a custom body string.


transformation:
  template:  
    bodyTransformation: 
      type: Body
      body: 'This is the value of the POD_NAME environment variable: {{env("POD_NAME")}}'

This template results in a body similar to This is the value of the POD_NAME environment variable: kgateway-proxy-http-844ff8bc4d-dh4pn.

To extract header information and add this information to the body, you can take multiple different approaches. The approach that is right for you depends on how you want to transform the body.

  • Inja functions:

    The following example uses an Inja function to access the value of a request header. This value is added to a custom body string.

    
    transformation:
      template:  
        bodyTransformation: 
          type: Body
          body: 'This is the value of the :path pseudo header: {{request_header(":path")}}.'

    This template results in a body similar to This is the value of the :path pseudo header: /json.

  • Extractors:

    The following example extracts the foo query parameter and adds that value to the body.

    
    transformation:
      template:  
        extractors: 
          foo: 
            # Use a nested capturing group to extract the foo query parameter
            header: ':path'
            # Use a nested capturing group to extract the query param
            regex: '(.*foo=([^&]*).*)'
            subgroup: 2
        bodyTransformation: 
          type: Body
          body: '{{ foo }}'

    This template adds the value of the foo query parameter to the body as follows:

    "data": "foo-value",
  • MergeExtractorsToBody:

    You can also use extractors to capture the information that you need from the request header, query parameters, or body, and add all of these values to the body. For more information, see the MergeExtractorsToBody tab.

Because the Solo Enterprise for kgateway control plane automatically parses a body as a JSON, you can directly access values from the body to inject them into your custom body that you want to return.

Assuming a body with the following format:

{
  "slideshow": {
    "editor": "Yours Truly",
    "date": "date of publication",
    "slides": [
      {
        "title": "Wake up to WonderWidgets!",
        "type": "all"
      },
      {
        "items": [
          "Why <em>WonderWidgets</em> are great",
          "Who <em>buys</em> WonderWidgets"
        ],
        "title": "Overview",
        "type": "all"
      }
    ],
    "title": "Sample Slide Show"
  }
}

You can use a transformation template similar to the following to extract the author, title, and slides attributes and add them to the response body.


transformation: 
  template:
    bodyTransformation: 
      type: Body
      body: '{"author": "{{ slideshow.author }}", "title": "{{ slideshow.title }}", "slides": "{{ slideshow.slides }}}'

The following body is returned after transformation:

{"author": "Yours Truly", "title": "Sample Slide Show", "slides": "[{"title":"Wake up to WonderWidgets!","type":"all"},{"items":["Why <em>WonderWidgets</em> are great","Who <em>buys</em> WonderWidgets"],"title":"Overview","type":"all"}]}% 

Add all extractors to the body. You can specify the hierarchy level, at which you want to add the extractor value by using dot notation for the extractor name. For example, an extractor that is named root is added at the body’s root level. An extractor that is named root.child is added as a nested element of root.

The following example defines a foo extractor that extracts the value of the foo header. The value is then merged into the response body on the root level.


transformation: 
  template:
    extractors: 
      foo: 
        header: 'foo'
        # Use a nested capturing group to extract the foo query parameter
        regex: '.*'
    bodyTransformation: 
      type: MergeExtractorsToBody

Assuming a request body in the following format:

{
  "Name": "Solo",
  "Favorites": {
    "Color": "Blue"
  }
}

The resulting body now contains the foo header as a root element.

{
  "Name": "Solo",
  "Favorites": {
    "Color": "Blue"
  },
  "foo": "foo-value-from-header"
}

Set this attribute if you want the Solo Enterprise for kgateway control plane to not parse or buffer the body. This option is useful if you do not want to transform or extract information from the request or response body, such as in cases where the payload is large.


transformation:
  template: 
    bodyTransformation: 
      type: Passthrough

dynamicMetadataValues

Use this attribute to define an Envoy Dynamic Metadata entry. This metadata can be used by other filters in the filter chain to implement custom behavior.

As an example, the following configuration creates a dynamic metadata entry in the com.example namespace with the key foo and the value of the foo header.


transformation:
  template: 
    dynamicMetadataValues: 
      - key: 'foo'
        value: '{{ header("foo") }}'
        metadataNamespace: "com.example"

Setting the metadataNamespace is optional. If not set, it defaults to the namespace of the Solo Enterprise for kgateway transformation filter name, such as io.solo.transformation.

A common use case for using this attribute is to include custom data in your access logs. For an example, see Enrich access logs.

escapeCharacters

Set to true to instruct Inja to preserve escaped characters in strings. This option is useful when using context from the request or response body to construct new JSON bodies.


transformation: 
  template:
    escapceCharacters: true

extractors

Use extractors to extract specific information from a header, query parameter, or the body that is sent in a request or response. You can later reference an extractor when injecting headers or transforming the request or response body.

Each extractor must use a regular expression (regex) to define which information you want to extract from the source. If the regex uses capturing groups, you can select the group match that you want to extract by using a subgroup attribute.

Extract values

Review the following examples to learn how to extract information from a header or body.

The following example defines two extractors:

  • foo: Uses a regular expression to extract the foo query parameter value from the :path pseudo header. The value is stored in the foo extractor.

  • bar: Captures the entire bar header value and stores that value in the bar extractor.

    
    transformation: 
      template:
        extractors:
          # The name of the extractor
          foo:
            # The extractor uses the :path pseudo header as the source, which contains the request/response path
            header: ':path'
            # Use a nested capturing group to extract the foo query parameter
            regex: '(.*foo=([^&]*).*)'
            # Select the second group match, which contains the value of the foo query parameter
            subgroup: 2
          # The name of the extractor
          bar:
            # The extractor uses the bar header as the source
            header: 'bar'
            # Capture the entire bar header
            regex: '.*'

The following example defines two extractors:

  • fullBody: Extracts the full body and stores the body information in the fullBody extractor.
  • bodyIdExtractor: Uses a regex to parse the entire body and to extract the value of the id field. This value is stored in the bodyIdExtractor.
    
    transformation: 
      template:
        extractors:
          # The name of the extractor
          fullBody:
            # his empty struct must be included to extract from the body
            body: true
            # Capture the entire body. This is useful for body information that cannot be parsed as a JSON
            regex: '.*'
          # The name of the extractor
          bodyIdExtractor:
            # This empty struct must be included to extract from the body
            body: true
            # The regex must match the ENTIRE body, else the extraction will not work
            # The [\s\S]* allows us to match any character, making the regex match the entire source.
            regex: '[\s\S]*id: ([0-9]{4})[\s\S]*'
            # Select the first subgroup match ([0-9]{4}) since we only want the id number
            subgroup: 1

Use extractors

You can refer to extractors in the headers or bodyTransformation attributes to transform a header or body.

For example, to access the value of the foo extractor when transforming a header, use the following syntax:

extractors:
  foo:
    header: ':path'
    regex: '(.*foo=([^&]*).*)'
    subgroup: 2
header: 
  x-foo: "{{ foo }}"

Similarly, to refer to it when transforming the body, use the following syntax:


extractors:
  foo:
    header: ':path'
    regex: '(.*foo=([^&]*).*)'
    subgroup: 2
bodyTransformation: 
  type: Body
  body: 'This is the foo value: {{ foo }}'

To merge all extractors to an existing body, you can use the type: MergeExtractorsToBody setting. For more information, see bodyTransformation.

Extractor modes

Solo Enterprise for kgateway provides predefined extractor modes that you can use to transform the extracted information more easily.

The Extract mode is enabled by default and allows you to extract the value of the specified capturing group. The regex expression must match the entire source you want to extract from. However, only the value in the specified subgroup is extracted.

The following example extracts the value of the foo query parameter. Note that because no extractor mode is set, it automatically defaults to EXTRACT. The extracted value is then added back as the x-foo header.


  extractors:
     # The name of the extractor
    foo:
      # The extractor uses the :path pseudo header as the source, which contains the request/response path
      header: ':path'
      # Use a nested capturing group to extract the foo query parameter
      regex: '(.*foo=([^&]*).*)'
      # Select the second group match, which contains the value of the foo query parameter
      subgroup: 2
  header: 
    x-foo: "{{ foo }}"

Replace the first occurrence of the nth capturing group with the text that is specified in replacement_text. The regex expression must match the entire source for the replacement to work. The subgroup field specifies n, the capturing group that you want to replace.

The following example defines a body extractor that captures the entire body and replaces the first occurrence of banana with orange. The transformed body is then returned.


extractors:
  # The name of the extractor
  BananaToOrange:
    # Capture the entire body and use that as the source for the transformation
    body: true
    # The extractor mode that replaces the first occurrence of the capturing group with 'replacement_text'
    mode: SingleReplace
    # Find 'banana' in the captured body
    regex: '.*(banana).*'
    # The regex capturing group that you want to replace with 'replacement_text'
    subgroup: 1
    # Replace the capturing group value with 'orange'.
    replacementText: 'orange'
bodyTransformation: 
  type: Body
  body: '{{ BananaToOrange }}'

For example, if the extractor processes a body that contains fruits, such as [apple, pear, banana, pineapple], the body is transformed to [apple, pear, orange, pineapple].

Replace all occurrences of the regex match pattern with the text that is specified in replacementText. The regex can match any part of the source. All matches are replaced.

The following example defines a body extractor that captures all instances of foo and replaces them with bar. The transformed body is then returned.


extractors:
  # The name of the extractor
  FooToBar:
    # Capture the entire body and use that as the source for the transformation
    body: true
    # Find all instances of 'foo' in the source body
    regex: 'foo'
    # The extractor mode that replaces the all occurences of 'foo'
    mode: ReplaceAll
    # Replace all instances of 'foo' with 'bar'
    replacementText: 'bar'
bodyTransformation: 
  type: Body
  body: '{{ FooToBar }}'

For example, if the extractor processes a body that contains the text foo foo foo, the body is transformed to bar bar bar.

headers

Apply transformation templates to request or response headers. The headers attribute allows you to specify the name of the resulting header key and applies a transformation template before determining the resulting header value. To transform the header value, you can leverage default and custom Inja functions, static values, or extractors.

To add a static value for the foo header, you can use the following transformation template.


transformation:
  template: 
    headers: 
      # The name of the resulting header and transformation template to apply. In this example,
      # you set the foo header to the value of 'static'. 
      foo: 'static'

This template results in a foo: static header.

The following example uses an Inja function to extract the value of the bar header and adds that value to the foo header.


transformation:
  template: 
    headers: 
      # The name of the resulting header and transformation template to apply. In this example, 
      # you retrieve the value of the 'bar' header and add that to the 'foo' header.
      foo: '{{ header("bar")}}'

The following example assumes a JSON body with the following format.

{
  "Name": "Solo",
  "Favorites": {
    "Color": "Blue"
  }
}

To extract the color attribute and add it as a color header, you can use the following transformation template:


transformation: 
  template:
    headers:
      color: '{{ Favorites.Color }}'

The following example uses an extractor to capture the value of the account header. The value is then appended with -solo and added back to the x-account-solo header.

To learn more about how to use extractors, see extractors.


transformation:
  template:
    extractors:
      account_id:
        header: account
        regex: '(\w+)'
        subgroup: 1
    headers:
      x-account-solo: "{{ account_id }}-solo"

headersToAppend

Append values to existing headers. For example, to add multiple values to the same header, use the following transformation template:


transformation: 
  template:
    headersToAppend:
    - key: Set-Cookie
      value: 'customcookie2={{ request_header("Host") }}'
    - key: Set-Cookie
      value: 'customcookie3={{ request_header("Host") }}'

For an example, see Append headers.

headersToRemove

You can define the headers that you want to remove from a request or response. For example, to remove the User-Agent header, use the following transformation template:


transformation: 
  template:
    headersToRemove: ["User-Agent"]

ignoreErrorOnParse

Set this attribute to true if you want the Solo Enterprise for kgateway control plane to parse the body as a JSON, but to not return an error if the body is not formatted as valid JSON.


transformation: 
  template:
    ignoreErrorOnParse: true

parseBodyBehavior

Select how you want the Solo Enterprise for kgateway control plane to parse the request or response body.

  • ParseAsJson: Instruct the control plane to parse the body as a JSON. Note that this is the default behavior in Solo Enterprise for kgateway. If parseBodyBehavior is not set, the body is automatically parsed as a JSON. If the control plane fails to parse the body as a JSON, a 400 Bad Request HTTP error is returned.

    
    transformation: 
      template:
        parseBodyBehavior: ParseAsJson
  • DontParse: Buffer the body as plain text, but do not parse it. This is useful for bodies that are not formatted as a JSON. Note that some of the templating features are not available when treating the body as plain text. To skip buffering and parsing of the body completely, see the passthrough setting in bodyTransformation.

    
    transformation:
      template:
        parseBodyBehavior: DontParse