🎉 Welcome to the Mobify DevCenter, our new home for v2.0 documentation and developer resources! (For now, the site is desktop-only.) Switch to old site
close
Mobify DevCenter
DocumentationAPI & SDK ReferenceHow-To Guides
search_icon_focus

Using Proxies

Note: Before you follow this guide, we recommend reading our overview of proxies.

Introduction

Imagine your Mobify app is hosted at www.example.com. You want to get data on a handbag from api.example.com. The API requires an API key, passed via the Authorization header. You use fetch from isomorphic-unfetch to make your code work both in the Express app and browsers:

import fetch from 'isomorphic-unfetch'
fetch('https://api.example.com/product/handbag.json', {
headers: {Authorization: 'API-TOKEN'}
})

There are several challenges with this approach. For one, in a browser, this request is cross-origin. You must configure api.example.com to respond with cross-origin resource sharing (CORS) headers to allow the browser to make the request. This request is also not a simple request in a browser: it requires a preflight request, which introduces an additional network roundtrip that harms performance. Another problem is that api.example.com may not use a CDN. It may take a long time to connect to it and it may not cache responses, further harming performance. Finally, you may not have access to logs for api.example.com, which would make it difficult to understand how its performance impacts your Mobify app.

Proxies get around these challenges by routing requests for APIs through Mobify’s CDN, which acts as an HTTP proxy server.

Using proxies, we can make api.example.com available at www.example.com/mobify/proxy/api/.

Setting the proxy settings

There are a few ways to set your proxy settings. It depends on whether you’re doing local development or running off cloud targets.

For local development:

  • Set the proxies in the package.json file
  • If you need to, you can override them with the environment variables (SSR_PROXY1, SSR_PROXY2, and so on)

For cloud targets, such as production or staging:

  • Set the proxies using the Mobify Cloud API
  • If the above step is not done, the published bundle’s proxy settings (in its package.json) will be used instead as the fallback

Setting proxies for local development

For local development, one of the ways to set your proxy settings is within package.json. In the scaffold, you can find it at packages/pwa/package.json:

{
"name": "mobify-proxy-test",
"mobify": {
"ssrParameters": {
"proxyConfigs": [{"host": "api.example.com", "path": "api"}]
}
}
}

mobify.ssrParameters.proxyConfigs is an array of proxies. A proxy object’s host sets the backend the request is proxied to. Its path sets how we refer to it in our app.

By default, a proxy connects to its backend over HTTPS. You can change this using the protocol key.

Proxies work both in local development and when deployed to a target. In local development, proxyConfigs is used to set up the Express app. This is managed by default in the scaffold. proxyConfigs is stored as part of a bundle.

Now, we can use our proxy by requesting data from it:

import fetch from 'isomorphic-unfetch'
fetch('/mobify/proxy/api/product/handbag.json', {
headers: {Authorization: 'API-TOKEN'}
})

We use the proxy by making a relative request to /mobify/proxy/$PROXYPATH/.

When you’re doing local development, to easily switch the proxy for different environments, you can use these environment variables to override the proxy settings: SSR_PROXY1 for the first proxy, SSR_PROXY2 for the second one, and so on. With those variables, you could create npm scripts to start the local server under different environments.

For example, the environment variable SSR_PROXY1=https://api-staging.example.com/api will be parsed into:

{
"protocol": "https",
"host": "api-staging.example.com",
"path": "api"
}

Setting proxies for cloud targets

For cloud targets, we recommend that you set the proxy settings in the Cloud via Mobify Cloud API. You’d want the Cloud (and not the bundle) to be the source of truth.

For example, imagine you have two targets:

  • production, which proxies requests to api.example.com.
  • staging, which proxies requests to api-staging.example.com.

As the value for proxyConfigs is part of the bundle, you’d need to be careful assessing which bundle you deployed to each target. That way, you wouldn’t accidentally point production at api-staging.example.com!

Instead, we can configure a target to override a bundle’s proxyConfigs using the Mobify Cloud API.

For example, in this scenario, we should use the API to set production’s proxyConfig to always reference api.example.com (ignoring the values set in package.json).

Important: Once set, never change the existing proxy settings for a target that is serving production traffic. Contact the Mobify Support Team if you would like to change proxy configuration for a production target.

Ideally, you’ll only need to have 1 bundle for all cloud targets. You don’t need to create a new bundle for each target just to modify the proxy settings. With the Mobify Cloud API, set your proxy settings in the Cloud.

Retrieving current proxy settings

Once the the proxy settings are set, later you can find out what they are by using the utility function getProxyConfigs(). For example, you might want to use different SFCC’s OCAPI client IDs depending on which environment you’re in.

import { getProxyConfigs } from "progressive-web-sdk/dist/ssr/universal/utils.js"
import { SalesforceConnector } from "@mobify/commerce-integrations/dist/connectors/sfcc"
const HOST_TO_CLIENT_ID = {
"api-staging.example.com": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
default: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
const CLIENT_ID = (function getClientId() {
const { host } = getProxyConfigs().find(c => c.path === "api")
return (
HOST_TO_CLIENT_ID[host] ||
HOST_TO_CLIENT_ID["default"]
)
})()
const connector = SalesforceConnector.fromConfig({
basePath: "/mobify/proxy/api/s/site_id/dw/shop/v17_8",
defaultHeaders: {
"x-dw-client-id": CLIENT_ID
}
})

The proxy’s HTTP request and response

Proxying a request changes the HTTP request made to the backend and HTTP response sent to the client to make it work transparently with your application code:

HTTP Request to the backend:

  • The /mobify/proxy/$PROXYPATH/ prefix is removed.
  • An HTTP header X-Mobify: true is added.

HTTP Response to the client:

  • An HTTP header X-Request-Url: $URL is added with the URL that was requested from your backend.
  • If the response from the backend was a redirect with an HTTP Status code between 301 and 307, and the response’s Location header was a relative or an absolute path whose host matches the proxy’s host, then the /mobify/proxy/$PROXYPATH/ prefix is added to the Location header.
  • If the response from the backend contains Set-Cookie headers whose domain matches the host of the proxy, they are updated to match it. For example, Set-Cookie: key=val; domain=api.example.com is updated to Set-Cookie: key=val; domain=www.example.com.
  • If the response from the backend contains an Access-Control-Allow-Origin header whose value matches the proxy’s host, it is updated. For example, Access-Control-Allow-Origin: https://api.example.com is updated to Access-Control-Allow-Origin: https://www.example.com.

Proxying supports the standard HTTP methods: HEAD|DELETE|POST|GET|OPTIONS|PUT|PATCH.

Proxied requests forward all query string parameters and headers including cookies to the backend.

Improving proxy performance with caching

By default, proxies don’t act as an HTTP cache. This allows them to be used transparently, without having to worry about incorrectly caching responses.

To use a proxy as an HTTP cache, change the path prefix used in your request from proxy to caching:

import fetch from 'isomorphic-unfetch'
fetch('/mobify/caching/api/product/handbag.json', {
headers: {Authorization: 'API-TOKEN'}
})

Caching the proxy’s HTTP request and response details

Caching proxy requests differ in notable ways from standard proxy requests:

Request to the backend:

The Cookie HTTP header is removed.

Response to the client:

Set-Cookie HTTP headers are removed.

Cached responses vary based on the following HTTP request headers:

  • Accept
  • Accept-Charset
  • Accept-Encoding
  • Accept-Language
  • Authorization
  • Range