Share Flows support DRY (Don't Repeat Yourself) by providing a reusable sequence of policies, the Shared Flow, that can be used by API proxies, either through a FlowCallout policy or configured as a Flow Hook.
Flow Hooks are merely a way to “hook” an existing Shared Flow to a particular “flow” (Pre or Post flows in Proxy or Target) across all proxies in an environment, without having to add it to the consuming API proxy.
It is important to note that the shared flow and policies can change independent of the API proxy that is using it.
There is no Shared Fault Flow. The Shared Flow executes within the consuming API proxy flow, therefore all faults must be managed in the consuming API proxy Fault Rules. But guess what, you can put Flow Callouts to Shared Flows there and finally be able to provide consistent and consolidated fault handling for all your API proxies!
Beyond reuse and consistency, API design simplification is a huge benefit, since a single Flow Callout can execute multiple policies in a Shared Flow.
The examples below are just a start, what other reuse cases do you have?
Shared Flows allow the API team to focus on the core aspects of the API, the gist of the API. The new API can then be augmented with Shared Flows or simply take advantage of a deployed Flow Hook.
Typical use cases for Shared Flows are:
Typical use cases for Flow Hooks are:
This example shows a Shared Flow to ensure consistency on the use of and API key across all proxies. The flow executes 2 policies a Validate API Key using X-APIKey header and an Assign Message to remove sensitive header information, a step often overlooked.
The shared flow
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <SharedFlow name="default"> <Step> <Name>Verify-API-Key</Name> </Step> <Step> <Name>AM-Remove-Sensitive-Headers</Name> </Step> </SharedFlow>
The Verify API Key policy
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <VerifyAPIKey async="false" continueOnError="true" enabled="true" name="Verify-API-Key"> <DisplayName>Verify API Key</DisplayName> <Properties/> <APIKey ref="request.header.X-APIKey"/> </VerifyAPIKey>The Assign Message policy
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <AssignMessage async="false" continueOnError="false" enabled="true" name="AM-Remove-Sensitive-Headers"> <DisplayName>AM - Remove Sensitive Headers</DisplayName> <Properties/> <Remove> <Headers> <Header name="x-apikey"/> </Headers> </Remove> <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables> <AssignTo createNew="false" transport="http" type="request"/> </AssignMessage>
Use in the Proxy PreFlow
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ProxyEndpoint name="default"> <Description/> <FaultRules/> <PreFlow name="PreFlow"> <Request> <Step> <Name>FC-ValidateAPIKey</Name> </Step> </Request> <Response/> </PreFlow> ...
The FlowCallout policy
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <FlowCallout async="false" continueOnError="false" enabled="true" name="FC-ValidateAPIKey"> <DisplayName>FC - ValidateAPIKey</DisplayName> <FaultRules/> <Properties/> <SharedFlowBundle>ValidateAPIKey</SharedFlowBundle> </FlowCallout>
This example shows how you can use Shared Flows in Fault Rules
<ProxyEndpoint name="default"> <Description/> <DefaultFaultRule name="DefaultFaultRule"> <Step> <FaultRules/> <Name>FC-DefaultFaultRule</Name> </Step> <AlwaysEnforce>true</AlwaysEnforce> </DefaultFaultRule> <FaultRules> <FaultRule name="FaultRules"> <Step> <!-- Put all your Fault Rules in one Shared Flow --> <Name>FC-FaultRules</Name> </Step> </FaultRule> </FaultRules>
The Shared Flow for FaultRules
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <SharedFlow name="default"> <Step> <Condition>fault.name = "FailedToResolveAPIKey" or fault.name = "InvalidApiKey"</Condition> <Name>AM-Fault-API-Key</Name> </Step> </SharedFlow>
This example shows how to configure Flow Hooks to support CORS. Recall that to fully support CORS, the pre-flight OPTIONS request must be processed in the Proxy PreFlow and CORS headers added in the Proxy PostFlow
The Shared Flow in the CORSPreFlow bundle
<SharedFlow name="default"> <Step> <Condition>(request.verb = "OPTIONS")</Condition> <Name>JS-Extract-CORS-Headers</Name> </Step> <Step> <Condition>(request.verb = "OPTIONS")</Condition> <Name>RF-CORS-Pre-Flight</Name> </Step> </SharedFlow>
The Shared Flow in the CORSPostFlow bundle
<SharedFlow name="default"> <Step> <Name>AM-Add-CORS-Headers</Name> </Step> </SharedFlow>
The configuration of the Flow Hooks
It may be useful for us to think about how this ties in with CI processes, and how shared flows can be tested and promoted between environments in isolation.
Good article!
One thing I have noticed that's not clear here, or in the docs, is that the menu to create new shared flows is missing from "new" Edge. It's currently only available from "classic" (or it's hidden well!)
Thanks @Neil Munro, I'm told we are planning to turn it on in New Edge on Jan 18th or maybe the week after.
@Sean Davis - the maven deploy plugin V1.1.4 now supports Shared flow deployments just like API Proxy. With this - you can now have the code in Version control and deploy to any given envirionment and plug it as part of your CI/CD process
Great article @Kurt Kanaskie
Good post.
How the flow is set-up for the fault rules will cause the DefaultFault to always be honored over the custom message from the other Fault Rules. You should remove the AlwaysEnforce stanza to cause the messages to be honored.
Hi @Vern Burton, thanks for the comment.
I omitted some details. Please note that there is a Flow Callout FC-DefaultFaultRule to a shared flow in the DefaultFaultRule. In that shared flow I have additional policies, such as adding CORS and most importantly an Assign Message that sets the standard error response structure.
In the FaultRules section the AM-Fault-API-Key policy just sets variables. Then in the FC-DefaultFaultRule this Assign Message sets the standard error response structure.
<AssignMessage async="false" continueOnError="false" enabled="true" name="AM-SetStandardFaultResponse"> <DisplayName>AM-SetStandardFaultResponse</DisplayName> <Properties/> <Set> <Payload contentType="application/json"> { "code": "{flow.errorCode}", "userMessage": "{flow.errorUserMessage}", "systemMessage": "{flow.errorSystemMessage}", "info": "http://developer.company.com/docs/errors#{flow.errorCode}" } </Payload> <StatusCode>{flow.errorStatusCode}</StatusCode> <ReasonPhrase>{flow.errorReasonPhrase}</ReasonPhrase> </Set> <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables> <AssignTo createNew="false" transport="http" type="response"/> </AssignMessage>
You can read more about this fault handling approach here: https://community.apigee.com/articles/23724/an-error-handling-pattern-for-apigee-proxies.html
Hi,
I have a use case to create a Shared Flow for logging (Syslog server).
What is the best suggested way to pass the Message (to be logged into Syslog) into the Shared Flow from Main Flow?
I see that FlowCallout policy has a <Properties> tag. Can I use it to set a property which can then be accessed from MainFlow?
Thanks,
Aneesh.
Hi @AneeshAnanthakrishnan - can you post this as a new question please ?