MuleSoft API Gateway Dynamic Passthrough Proxy
Learn to create a dynamic passthrough proxy from a static proxy with Mule.
Join the DZone community and get the full member experience.
Join For FreeThis blog describes how to create a dynamic passthrough proxy in Mule which can be used to expose services from different backend systems.
Problem Statement
Exposing backend services through API Gateway are frequently needed to enable various systems to connect with each other. A Mule developer can create a passthrough proxy easily with points and clicks. The high-level steps are listed below. One can follow the same steps to create additional proxies that expose different service endpoints. However, when the number of proxies created this way grows to large, the overhead to create, track, and maintain these proxies could be overwhelming. A natural consideration of a developer would be how these highly repetitive proxies can be refactored into a dynamic one. Luckily, an existing static proxy can be turned into a dynamic with a few twists.
You may also enjoy: API Gateway to the Rescue
Creating a Dynamic Proxy
To turn a static proxy into a dynamic proxy, two things need to happen:
- Move the service endpoint configuration from the project properties file outside of the project and load them at runtime.
- The dynamic proxy needs to be able to map incoming requests to related backend services.
For the first step, a convenient option is Mule Runtime properties.
config.properties
api.id=15844828
proxy.path=/*
proxy.port=8081
http.private.port=8091
proxy.responseTimeout=10000
#implementation.host= samples.openweathermap.org
#implementation.port=443
#implementation.path=/
#implementation.protocol=HTTPS
Mule Runtime properties
weather.implementation.protocol=HTTPS
weather.implementation.port=443
weather.implementation.host=samples.openweathermap.org
weather.implementation.path=/
For the second, a segment of the URL path can be used to map to the related service endpoint configurations in the first step.
https://<apigateway-host-name>/<Mule-app-name>/<service-name>/<service-path>
ex: https://<host>/apis/weather/data/2.5/weather?q=Plano,us&appid=b6907d289e10d714a6e88b30761fae22
Steps to Create a Proxy
Typical steps needed to create a proxy in enterprise development environments
- Create RAML in Designer
- Create the proxy in API manager and deploy to Runtime
- Download the Runtime jar
- Import the jar into Anypoint Studio for development
- Unit test in Anypoint Studio
- Check in the project into version control system
- Update CI/CD pipeline to pickup the new project for build and deployment
- Apply and configure policies in API Manager
- Verify the proxy passes connectivity tests
Benefits of a Dynamic Proxy
- Development cost: Dynamic proxy turns code development into runtime configuration. This can be done in a few minutes. A static proxy could take about 1-2 days.
- API Management: A reduced number of items to be managed in API and Runtime manager.
- Resource usage: Increase resources/worker sharing and reduce licensing costs, especially in scenarios with a large number of services with light transactions.
Disadvantages of a Dynamic Proxy
- Policies: Mule provided policies can only be applied at the proxy level, though custom policies can be adapted to apply at the service level.
- Analytics: By grouping multiple services into a single proxy, analysis at the service level could be challenging.
- Performances: Worker sharing requires more planning and performance assessments.
Mule Flow Code
http-proxy.xml
<ee:transform doc:name="Transform Message" doc:id="0f6e59d2-bdb9-4558-9e5a-e660519b2c37" >
<ee:message >
<ee:set-payload ><![CDATA[%dw 2.0
output application/java
---
{
}]]></ee:set-payload>
</ee:message>
<ee:variables >
<ee:set-variable variableName="targetConf" ><![CDATA[%dw 2.0
import * from dw::System
import * from dw::Runtime
import * from dw::core::Strings
var reqPath = attributes.requestPath[1 to sizeOf(attributes.requestPath)-1]
var proxyName = substringBefore(reqPath, "/")
var evarTargetHost = proxyName ++ ".implementation.host"
var evarTargetPort = proxyName ++ ".implementation.port"
var evarTargetPath = proxyName ++ ".implementation.path"
var evarTargetProtocol = proxyName ++ ".implementation.protocol"
output application/json
---
{
"targetAPIName" : proxyName,
"targetRequestPath" : substringAfter(reqPath, "/"),
"targetHost" : prop(evarTargetHost),
"targetPort" : prop(evarTargetPort),
"targetPath" : prop(evarTargetPath)
}]]></ee:set-variable>
</ee:variables>
</ee:transform>
<choice doc:name="Choice" doc:id="b8ee2920-784a-4fb6-a033-45c3be7ae4bf" tracking:enable-default-events="true">
<when expression="#[vars.targetConf.targetHost != null]">
<http:request config-ref="http-request-config" method="#[attributes.method]" path="#[vars.targetConf.targetRequestPath]">
<http:response-validator>
<http:success-status-code-validator values="0..599" />
</http:response-validator>
</http:request>
outbound-config.xml
<http:request-config name="http-request-config" basePath="#[vars.targetConf.targetPath]">
<http:request-connection host="#[vars.targetConf.targetHost]" port="#[vars.targetConf.targetPort]"
protocol="HTTPS" />
</http:request-config>
Further Reading
Opinions expressed by DZone contributors are their own.
Comments