Application Modules

API Proxying

Define proxy.toml rules to securely forward browser requests to external APIs, with dynamic graph-driven routes.

API Proxying (proxy.toml)

When developing custom web applications, you may need to securely communicate with external APIs (like internal billing systems or metadata services) without exposing their endpoints or credentials to the browser. It is also very useful to compare the declared state in the graph with the real state of services.

You can define backend proxy rules by creating a proxy.toml file directly inside your app/ directory.

# app/proxy.toml
[[route]]
path = "/api/billing"
target = "http://internal-billing-service:8080/v1"
forward_headers = ["Content-Type", "Accept"]
inject_headers = { "Authorization" = "Bearer ${env.BILLING_API_TOKEN}" }

When a user’s browser requests /apps/my-module/api/billing/invoice, the rescile server intercepts it and securely proxies the request to http://internal-billing-service:8080/v1/invoice.

Graph-Driven Proxy Routes:

You can mix static routes with dynamic routes that instruct rescile to query the graph (e.g., fetching all nodes with a specific label) and generate a distinct proxy route for each matching graph node. This mirrors the “Configuration as Code” paradigm.

Use origin_resource to match nodes by label, and optionally match_on to filter those nodes just like in architectural models. The properties of the matching node are exposed via Tera templating:

# app/proxy.toml

# Dynamic routes generated from the graph
[[route]]
origin_resource = "provider"
# Optional: Filter the graph nodes just like in models
match_on = [ { property = "domain", value = "core" } ]

# The properties of the matching vertex are exposed via Tera templating
path = "/api/{{ origin_resource.name }}/*"
target = "{{ origin_resource.api }}/"

[route.inject_headers]
# The importer renders the Tera parts when flattening the routes,
# leaving the `${env...}` regex intact for the proxy runtime.
"Authorization" = "Bearer ${env.{{ origin_resource.name | upper }}_TOKEN}"

Dynamic Request Signatures:

For APIs that require every request to be cryptographically signed (such as Exoscale’s v2 API), you can use Tera templating inside the inject_headers values. The templating context provides the request object (with method, path, query, body, headers, and timestamp) and env variables. You can combine this with custom filters like hmac_sha256 to construct complex authorization headers dynamically:

# app/proxy.toml
[[route]]
path = "/api/exoscale"
target = "https://api-ch-dk-2.exoscale.com/v2"
forward_headers = ["Accept", "User-Agent"]
[route.inject_headers]
"Content-Type" = "application/json"
"Authorization" = """
{%- set expires = request.timestamp + 600 -%}
{%- set msg = request.method ~ " " ~ request.path ~ "\n" ~ request.body ~ "\n" ~ request.query_values ~ "\n\n" ~ expires -%}
{%- set sig = msg | hmac_sha256(key=env.EXOSCALE_API_SECRET, encoding="base64") -%}
EXO2-HMAC-SHA256 credential={{ env.EXOSCALE_API_KEY }}{% if request.signed_query_args %},signed-query-args={{ request.signed_query_args }}{% endif %},expires={{ expires }},signature={{ sig -}}
"""

You can also use templating inside the target parameter to dynamically construct URLs:

# app/proxy.toml
[[route]]
path = "/api/cloudflare"
target = "https://api.cloudflare.com/client/v4/zones/{{ env.CLOUDFLARE_ZONE_ID }}/"
forward_headers = ["Accept", "Content-Type"]
inject_headers = { "Authorization" = "Bearer ${env.CLOUDFLARE_API_TOKEN}" }

Forward header

Forwarding authentication headers is also usable for authentication header forwarding.

forward_headers = ["Accept", "User-Agent", "Authentication"]