I work for Microsoft as an App Innovation Cloud Solution Architect in the UK. The posts and comments on this site are my own opinions and are not endorsed in any way by my employer.

Using Liquid filters in Azure API Management.

API Management is a great tool for “fronting” your companies APIs and has some powerful tools to shape and transform those operations. One such tool is the set-body policy. This policy lets you shape either the request sent to the backend (if placed in the <inbound> element) or the response sent to the client (if called in the <outbound> section). The policy works in one of 2 ways, using code snippets (C# with a subset of the .net framework) or using liquid templates. The code snippets are great for minor modifications (e.g. remove or add a property to the existing body) but liquid templates give you an editor experience that is closer to the expected output.

At a high level, lets assume I want to call the ICanHazDadJoke API. (I am a sucker for terrible joke!) The search api

GET https://icanhazdadjoke.com/search?term=cat
Accept: application/json

will return the following json payload (truncated results)..

{
    "current_page": 1,
    "limit": 8,
    "next_page": 2,
    "previous_page": 1,
    "results": [
        {
        "id": "O7haxA5Tfxc",
        "joke": "Where do cats write notes?\r\nScratch Paper!"
        },
        {
        "id": "TS0gFlqr4ob",
        "joke": "What do you call a group of disorganized cats? A cat-tastrophe."
        }
    ],
    "search_term": "cat",
    "status": 200,
    "total_jokes": 10,
    "total_pages": 2
}

I have created a new API Operation in APIM and configured the backend to point at icanhazdadjoke.com.

lets assume instead of returning this object we just want to return a json array of the jokes. We can do this with a set-body policy in APIM (inside the <outbound> section).

<set-body template="liquid">{
    "results": [
        {% JSONArrayFor joke in body.results %}
            "{{ joke.joke }}"
        {% endJSONArrayFor %}
    ]
}</set-body>

We are using JSONArrayFor here instead of for to ensure no trailing comma is left. My API call now returns something like this…

{
    "results": [
                  
            "Where do cats write notes?
Scratch Paper!",
        
            "What do you call a group of disorganized cats? A cat-tastrophe." 
    ]
}

Almost there, but when I try to test this using the excellent rest-client extension in VSCode, it complains that the body isn’t a valid JSON payload! The reason is the rogue \r\n in the first joke, so we need to strip out/convert any newlines before we return the response. Enter liquid filters!

According to the documentation I should be able to change my set-body policy to use one or more filters like this (I am using both to really make sure they are gone 😀, actually newline_to_br adds a br element in front of any \r\n but we still need to get rid of them, this way the intended formatting is preserved as html)..

<set-body template="liquid">{
    "results": [
        {% JSONArrayFor joke in body.results %}
            "{{ joke.joke | newline_to_br | strip_newlines }}"
        {% endJSONArrayFor %}
    ]
}</set-body>

Run this through APIM and there is no change to the output! How odd! Some more reveals some odd truths, the implementation of liquid in APIM is based on the dotliquid library (source). Nestled amongst the documentation for dotliquid is a reference to Filter and Output Casing, which states that

“DotLiquid uses this same convention by default, but can also be changed to use C# naming convention, in which case output fields and filters would be referenced like so “

hmmm, I wonder if the C# naming convention is the default for APIM, if so then the following set-body policy should work..

<set-body template="liquid">{
    "results": [
        {% JSONArrayFor joke in body.results %}
            "{{ joke.joke | NewlineToBr | StripNewlines }}"
        {% endJSONArrayFor %}
    ]
}</set-body>

🎉ta-da🎉 valid json.

{
    "results": [
        "Where do cats write notes?<br />Scratch Paper!",
        "What do you call a group of disorganized cats? A cat-tastrophe."
    ]
}