Skip to main content

App Proxy

in Apps
Authors list
Diterbitkan: 1 Feb 2022|Last updated: 6 Agu 2024

The purpose of the apps proxy is to safely allow your app to communicate with the outside world in a controlled way. The four main aspects of the proxy are:

  • Whitelist – The proxy is essentially a whitelist of allowed URLs and HTTP methods that you might use when communicating with an API. These URLs are defined concretely or as regular expressions

  • Timeouts – For each URL in the proxy configuration you can configure a timeout in seconds. This is the maximum amount of time the proxy will wait for a response from another server

  • Settings Injection – Some APIs require some form of authentication, and this is usually based on a “secret” or “key” that must be configured.

  • State injection - State and user state set by an app may be injected by the proxy

ConfigurationCopy link to Configuration to clipboard

Configuring the proxy is done inside the app manifest in the root of your project. You can configure the proxy with as many "rules" as you like, and each rule requires the following parameters:

  • url - the URL pattern of the request (literal string or regular expression) (required)

  • methods - an array of HTTP methods allowed by this rule (required)

  • timeout - maximum timeout of this rule in seconds (required)

Below is an example manifest proxy configuration:

{ "name": "my_app", "title": "My App", "description": "Brief description of my app", "version": "1.0.0", "scope": "agent", "isSingleInstall": true, "targets": [{ "target": "ticket_sidebar", "entrypoint": "index.html" }], "settings": { "api_key": { "title": "API Key", "description": "Key for an API", "type": "string", "isRequired": true, "isBackendOnly": true } }, "proxy": { "whitelist": [{ "url": "https://example.com/api/.*", "methods": ["GET", "POST"], "timeout": 10 }] } }
copy

In this example the proxy is configured with a single rule, allowing GET or POST on any sub path of the URL regular expression pattern https://example.com/api/.* (with a 10 second timeout)

Setting InjectionCopy link to Setting Injection to clipboard

In the previous example we also configured a setting with the name api_key, with the isBackendOnly flag set to "true". This means that we can use this setting exclusively in out proxy to pass a secret along with the request. To do this, we use the placeholder format "__<setting_name>__". So, in this example our placeholder would be "__api_key__". Here's an example of us injecting the setting into the URL of an outbound request:

import { useEffect, useState } from "react"; import { useDeskproAppClient, proxyFetch, Fetch, } from "@deskpro/app-sdk"; export const MyComponent = () => { const [users, setUsers] = useState<string[]>([]); const { client } = useDeskproAppClient(); useEffect(() => { // Is the Deskpro app client initialised? if (!client) { return; } // First, we need to get a special version of fetch() that is authorised to use the apps proxy proxyFetch(client).then((fetch: Fetch) => { // Perform GET request against the API, replacing __api_key__ with the associated setting value fetch("https://example.com/api/users?key=__api_key__").then((res: Response) => { // Get data from the API and set state res.json().then((data) => { setUsers(data); }); }); }); }, [client]); return ( <> {users.map((user: string, idx: number) => ( <div key={idx}>{user}</div> ))} </> ); };
copy
Note

You can also inject settings into whitelist URLs. This is handy for when you may have a user-defined setting that affects an API URL in some way, e.g.

"proxy": { "whitelist": [{ "url": "https://__domain__/api/.*", "methods": ["GET", "POST"], "timeout": 10 }] }
copy

Where __domain__ is a setting, representing a user-defined domain name used for API requests that must be validated by the proxy.

Advanced Settings InjectionCopy link to Advanced Settings Injection to clipboard

Injecting plain string values is fine for the majority of use cases, however there are some occasions where you need to encode a value or even concatenate it with another setting or even a string literal. An example of this is in HTTP basic authorization.

Let's say we have a header like this:

Authorization: Basic dGVzdHVzZXI6dGVzdGtleQ==
copy

As you can see, the authorization token itself is base64 encoded. Not only that, it has a string literal of : separating the two values. The plain, unencoded value looks like this:

Authorization: Basic testuser:testkey
copy

When injecting settings we can do the following:

Authorization: Basic __username + ':' + api_key.base64__
copy

Ok, so there's quite a lot going on in this placeholder expression; let's break it down.

  1. We have two settings, "username" and "api_key"

  2. We use the "+" operator to concatenate ":" in-between them

  3. We append the "base64" filter to the end using "." (dot) notation

Available Filters:

  • base64 - base64 encode the setting value

  • urlencode - URL encode the setting value

You can also chain filters together, like this:

__username.base64.urlencode__
copy

State InjectionCopy link to State Injection to clipboard

Similar to settings injection, we may also inject state values into outbound requests if the state values are "scalar", i.e. string or number. We use a different delimiter format for state values as state names may conflict with those of settings. These formats are: "[[<state_name>]]" or "[user[state_name]]" for user state.

In the following example we'll inject the user state value for params/my-secret:

import { useEffect, useState } from "react"; import { useDeskproAppClient, proxyFetch, Fetch, } from "@deskpro/app-sdk"; export const MyComponent = () => { const [users, setUsers] = useState<string[]>([]); const { client } = useDeskproAppClient(); useEffect(() => { // Is the Deskpro app client initialised? if (!client) { return; } // First, we need to get a special version of fetch() that is authorised to use the apps proxy proxyFetch(client).then((fetch: Fetch) => { // Perform GET request against the API, replacing [user[params/my-secret]] with the associated state value fetch("https://example.com/api/users?key=[user[params/my-secret]]").then((res: Response) => { // Get data from the API and set state res.json().then((data) => { setUsers(data); }); }); }); }, [client]); return ( <> {users.map((user: string, idx: number) => ( <div key={idx}>{user}</div> ))} </> ); };
copy

ResponsesCopy link to Responses to clipboard

Sometimes we encounter errors when using 3rd party APIs. However, as we're communicating via a proxy, we need to know if the response we received was from the remote system or the proxy itself. For this, we can check the X-Original-Status response header. If this header is set, then the response originated from the remote system. If this value is not set, then the HTTP status code of the reponse is from our proxy.

MembantuUnhelpful
next pageApp UI & Theme
previous pageEntity Associations

Please log in or register to submit a comment.