I wanted to document a pattern that I’ve been using since Apigee introduced the concept of “DefaultFaultRule” (too long ago - apologies I have been lazy). I have been explaining this to many Apigeeks so it is used in many Apigee projects but I don’t believe it is properly documented anywhere.
The reasons why I use this pattern are:
- centralised error handling
- prevents code duplication/repeat
- simple and leaner FaultRule definitions
- easy “catch-all” error handling
The Problem
In conventional error handling implementation, we tend to put all handler logic within FaultRule element - this has the advantage of creating a self contained error handling logic for a specific error condition.
<FaultRule name="Expired Access Token">
<Condition>(fault.name = "access_token_expired")</Condition>
<Step>
<Name>ServiceCallout.LogError</Name>
</Step>
<Step>
<Name>RaiseFault.ExpiredAccessToken</Name>
</Step>
</FaultRule>
Above FaultRule handles access token expiry error scenario by logging the error and returning a clean error response specific to access token expiry scenario. Let’s assume the RaiseFault policy looks like this:
<RaiseFault name="RaiseFault.ExpiredAccessToken">
<FaultResponse>
<Set>
<Headers>
<Header name="Content-Type">application/json</Header>
</Headers>
<Payload contentType="application/json">{
"code": "400",
"message": "Access token has expired",
"info": "https://developers.myapi.com/e400.01"
}</Payload>
<StatusCode>400</StatusCode>
<ReasonPhrase>Bad Request</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>
However as the number of FaultRules increase, we will need to duplicate these policies and make minor modifications in them to handle different type of errors, e.g. invalid access token error.
<FaultRule name="Expired Access Token">
<Condition>(fault.name = "access_token_expired")</Condition>
<Step>
<Name>ServiceCallout.LogError</Name>
</Step>
<Step>
<Name>RaiseFault.ExpiredAccessToken</Name>
</Step>
</FaultRule>
<FaultRule name="Invalid Access Token">
<Condition>(fault.name = "invalid_access_token")</Condition>
<Step>
<Name>ServiceCallout.LogError</Name>
</Step>
<Step>
<Name>RaiseFault.InvalidAccessToken</Name>
</Step>
</FaultRule>
ServiceCallout.LogError policy has been referenced one more time for “invalid access token” scenario so we can log that error type. I also need to copy/paste RaiseFault policy and change the message to say “Access token is invalid”.
If in the future I want to change the structure of the error responses, I will need to change all RaiseFault policies one by one. I can get rid of these unnecessary duplications by reusing both policies for all error scenarios. I can do that by templating both policies to use variables to fill in actual data. I also want to refer those policies once in proxy definition rather than referencing them in every FaultRule.
This is where DefaultFaultRule come in. Its official definition is “A default fault rule acts an exception handler for any error that is not explicitly handled by another fault rule”. I would also translate this as “A default fault rule acts as a common FaultRule which is executed if no other FaultRule has executed RaiseFault policy”. Read more about this here: http://docs.apigee.com/api-services/content/fault-handling#creatingfaultrules-definingthecustomerrormessagereturnedfromafaultrule
The Solution
So here is the refactored error handling logic using DefaultFaultRule construct:
<FaultRules>
<FaultRule name="Expired Access Token">
<Condition>(fault.name = "access_token_expired)</Condition>
<Step>
<Name>AssignMessage.SetExpiredAccessTokenErrorVariables</Name>
</Step>
</FaultRule>
<FaultRule name="Invalid Access Token">
<Condition>(fault.name = "invalid_access_token")</Condition>
<Step>
<Name>AssignMessage.SetInvalidAccessTokenErrorVariables</Name>
</Step>
</FaultRule>
</FaultRules>
<DefaultFaultRule name="all">
<AlwaysEnforce>true</AlwaysEnforce>
<Step>
<Condition>(flow.myapi.error.code = null)</Condition>
<Name>AssignMessage.SetInternalServerErrorVariables</Name>
</Step>
<Step>
<Name>ServiceCallout.LogError</Name>
</Step>
<Step>
<Name>RaiseFault.Json</Name>
</Step>
</DefaultFaultRule>
In above refactored snippet, FaultRule elements are very lean - they are only responsible for setting data relevant to that particular error scenario. Those variables will then get used by the common policies under DefaultFaultRule.
Here is an example of AssignMessage.SetExpiredAccessTokenErrorVariables policy:
<AssignMessage name="AssignMessage.SetExpiredAccessTokenErrorVariables">
<AssignVariable>
<Name>flow.myapi.error.code</Name>
<Value>400</Value>
</AssignVariable>
<AssignVariable>
<Name>flow.myapi.error.message</Name>
<Value>access token has expired</Value>
</AssignVariable>
<AssignVariable>
<Name>flow.myapi.error.info</Name>
<Value>https://developers.myapi.com</Value>
</AssignVariable>
<AssignVariable>
<Name>flow.myapi.error.status</Name>
<Value>400</Value>
</AssignVariable>
<AssignVariable>
<Name>flow.myapi.error.reason</Name>
<Value>Bad Request</Value>
</AssignVariable>
</AssignMessage>
So these variables can then be used by the actual RaiseFault policies that is common to all error type:
<RaiseFault name="RaiseFault.Json">
<FaultResponse>
<Set>
<Headers>
<Header name="Content-Type">application/json</Header>
</Headers>
<Payload contentType="application/json">{
"code": "{flow.myapi.error.code}",
"message": "{flow.myapi.error.message}",
"info": "{flow.myapi.error.info}"
}</Payload>
<StatusCode>{flow.myapi.error.status}</StatusCode>
<ReasonPhrase>{flow.myapi.error.reason}</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>
RaiseFault.Json is responsible for defining the structure of the json error response while the data comes from the individual FaultRule.
Catching Unhandled Errors
When an error is thrown from any Apigee policy or custom code that is not handled by any of the FaultRule conditions, Apigee will start executing the policies under DefaultFaultRule. In this situation we would like to return a stock 500 response to the consuming apps instead of the default Apigee response so that we are consistent in our error responses for all cases.
This is handled by the following Step definition in DefaultFaultRules:
<Step>
<Condition>(flow.myapi.error.code = null)</Condition>
<Name>AssignMessage.SetInternalServerErrorVariables</Name>
</Step>
Which basically says “if one of the variables that should have set for this error is null, assume this error is not handled by any FaultRules and set the variables to some stock 500 response”.
Here is a sample implementation of AssignMessage.SetInternalServerErrorVariables policy:
<AssignMessage name="AssignMessage.SetUnhandledErrorVariables">
<AssignVariable>
<Name>flow.myapi.error.code</Name>
<Value>500</Value>
</AssignVariable>
<AssignVariable>
<Name>flow.myapi.error.message</Name>
<Value>internal server error</Value>
</AssignVariable>
<AssignVariable>
<Name>flow.myapi.error.info</Name>
<Value>https://developers.myapi.com</Value>
</AssignVariable>
<AssignVariable>
<Name>flow.myapi.error.status</Name>
<Value>500</Value>
</AssignVariable>
<AssignVariable>
<Name>flow.myapi.error.reason</Name>
<Value>Internal Server Error</Value>
</AssignVariable>
</AssignMessage>
Don’t forget that if multiple fault rules have a condition that evaluates to true, then the last of those fault rules executes.
Errors in Custom Code
How can we utilise DefaultFaultRule logic when we are handling errors in custom code, e.g. JS? All we need to do is to set variables that we need and force Apigee to halt flow processing and go straight to FaultRules:
if (...) {
context.setVariable('flow.myapi.error.message', 'xyz parameter should be boo');
... set other error variables similar to AssignMessage policies
throw new Error(); //halt current execution flow
}
When this error is thrown in JS, Apigee will start executing FaultRules. None of the conditions will match which is a good thing as we have already set all variables we need. DefaultFaultRule will be executed to perform logic necessary.
If you find it inconsistent to define the error message in your custom code rather than in AssignMessage policies, you can set variables indicating type of the error and other parameters in your custom code and allow another policy in FaultRules to define the format of the error message. Don’t forget that the main point here is to let all errors flow through to FaultRules and a single policy within DefaultFaultRules to package everything in an HTTP response.
Possible Improvements:
- An AssignMessage policy that overrides and sets a new HTTP response can also be used instead of RaiseFault policy (as commented by @Dino below). You don’t need to use RaiseFault in a FaultRule… the flow is already in Fault.
- If you are logging errors to an HTTP endpoint, consider using async JavaScript instead of ServiceCallout policy. ServiceCallout will do a synchronous HTTP call to the log endpoint which is not needed here. You will get better performance out of async HTTP calls to the log servers.
- If you can use syslog for pushing log messages to the log servers or file (private cloud), consider using MessageLogging policy within PostClientFlow.


