We make use of Azure Durable Functions for a range of long running API tasks, but one particular issue has bugged us for some time. This was how to get the status updates of a Function back to the calling code when that Function is part of a more complex (and secure) n-tier architecture.
The built in code for a Durable Function will accept the initial request to launch processing and then return an object with a number of properties on it (instanceId, status URL etc). The status Url can be used in combination with the InstanceId to poll the Function to get progress updates, which is pretty cool, but only if you are happy exposing your function to that calling code directly or publicly.
For reference our Function API's sit behind an API Management instance, which immediately means the URL that is returned by the function app is not reachable as demonstrated below:
The status URL can be constructed by convention so you don't need to rely on the built in response that is generated for you, so you could add a dedicated back end to your API Management instance, to forward traffic to the function status URL. But in our case we deploy all of our (many) API's via terraform, having one non-conventional URL would require some nasty forks in that codebase which seemed wrong.
It appeared obvious to us that being able to just get the status of a Durable Function app was something people would want to do, but the documentation didn't seem to point to any method to do that and most forums had people searching for an answer but with no joy.
The Answer
It turns out there is a really elegant solution to this problem, which we discovered after trawling through the Azure source code and comparing the various implementations (C# vs Python).
If you have a Durable Function app, in the same way that the Durable processing can be instantiated by a HTTP POST function, the status of a Durable function can be obtained by any other Function inside the app, this is because the Durable runtime is shared. This means we can create a dedicated HTTP function that is responsible for just returning the status of an orchestration execution.
As a simple Illustration of how to do this using Python, create a new HTTP function and then:
2. The Http entry point of the function can then be extended to include that property as a parameter.
For us our API now looks like this:
- /api/dothing --> Durable Function HTTP POST Start
- /api/dothing/status/{instanceId} --> Sibling HTTP GET Function
That means we maintain our API Management pattern, our Restful API implementation and we definitely do not expose our function app to the wild.