This is the legacy documentation of Project-level Custom Applications, which is in maintenance mode. Visit the new documentation for Org-level Custom Applications.

BETA

Example Deployment with Vercel

This deployment example refers to the Vercel serverless deployment, formerly ZEIT Now.

Prerequisites

Before you get started, you need to have:

We're going to deploy the application under the domain mc-examples-starter.vercel.app.

The mc-examples-starter.vercel.app domain is already in use. Pick a different one if you choose to deploy using Vercel.

Configuration

To start, we need to update the custom-application-config.json file with the production URL:

custom-application-config.jsonjson
{
"name": "mc-examples-starter",
"entryPointUriPath": "examples-starter",
"cloudIdentifier": "gcp-eu",
"env": {
"production": {
"url": "http://mc-examples-starter.vercel.app"
}
}
}

To configure Vercel (formerly Zeit Now) deployments, we can use a vercel.json file. It looks something like this:

{
"version": 2,
"public": true,
"name": "mc-examples-starter.vercel.app",
"alias": "mc-examples-starter.vercel.app",
"regions": ["bru"],
"builds": [
{
"src": "public/**",
"use": "@vercel/static"
}
],
"routes": [
{
"src": "/(.*).(js.map|js|css|txt|html|png)",
"dest": "/public/$1.$2",
"headers": {
"cache-control": "s-maxage=31536000,immutable"
}
},
{
"src": "/(.*)",
"dest": "/public/index.html",
"headers": {
"Cache-Control": "no-cache"
}
}
]
}

Some fields may vary based on your setup and requirements, for example public, regions, etc.

However, that won't work just yet as the Custom Application does not have an index.html after building the production bundles. To make it work, we need to compile the application first.

Compile the application

To be able to deploy the Custom Application to the Vercel platform, the Custom Application needs to be compiled.

mc-scripts compile-html

The command above does what we need: it compiles the index.html using the JavaScript bundle references (after running mc-scripts build) and the runtime configuration. At this point the index.html file is ready for production usage.

However, the Custom Application needs to instruct the User-Agent (the browser) to enforce certain security measures, using HTTP headers (done automatically by the application config).

Because of that, the vercel.json file cannot be defined statically. Instead, it needs to be generated programmatically when the Custom Application is built and compiled. To achieve that, we need to implement a transformer function.

Generate vercel.json using a transformer function

The compile-html command accepts an option transformer which we can use to pass the filesystem path to our transformer function.

We assume that the transformer function is defined at the following location: ./config/transformer-vercel.js.

mc-scripts compile-html \
--transformer ./config/transformer-vercel.js

The purpose of the transformer function is to generate the final vercel.json given the compiled values passed to the function.

// Function signature using TypeScript
type TransformerFunctionOptions = {
// The runtime environment specified within the application config.
env: Json;
// The compiled HTTP headers, including the Content-Security-Policy headers.
headers: Json;
// The final HTML content of the `index.html`.
indexHtmlContent: string;
}
type TransformerFunction = (options: TransformerFunctionOptions) => void;

The main export of the file should be the transformer function.

transformer-vercel.jsJavaScript
module.exports = function transformer(options) {
// ...
}

With that in mind, we can implement the transformer function and write the vercel.json config into the filesystem.

./config/transformer-vercel.jsJavaScript
const fs = require('fs');
const path = require('path');
const rootPath = path.join(__dirname, '..');
module.exports = function transformer({ headers }) {
const config = {
version: 2,
public: true,
name: 'mc-examples-starter.vercel.app',
alias: 'mc-examples-starter.vercel.app',
regions: ['bru'],
builds: [
{ src: 'public/**', use: '@vercel/static' },
],
routes: [
{
src: '/(.*).(js.map|js|css|txt|html|png)',
dest: '/public/$1.$2',
headers: { 'Cache-Control': 's-maxage=31536000,immutable' },
},
{
src: '/(.*)',
dest: '/public/index.html',
headers: { 'Cache-Control': 'no-cache', ...headers },
},
],
};
fs.writeFileSync(
path.join(rootPath, 'vercel.json'),
JSON.stringify(config, null, 2),
{ encoding: 'utf8' }
);
};

Adding fallback routes

This step is optional and does not prevent the application to be used within the Merchant Center. However, it's recommended to do so to avoid unexpected behaviors in case the URL, where the Custom Application is hosted, is accessed directly.

Accessing the Custom Application directly at https://mc-examples-starter.vercel.app won't work, as the application requires the user to log in and thus tries to redirect to the /login route at the same domain.

To prevent that, we can add a dummy fallback route for the login|logout routes. This is only meant to inform the user that the Custom Application cannot be used standalone.

./config/fallback-route.jsJavaScript
module.exports = function fallbackRoute(request, response) {
response.end(
'This is not a real route. If you are seeing this, you most likely are accessing the custom application\n' +
'directly from the hosted domain. Instead, you need to access the custom application from within the Merchant Center\n' +
'domain, as custom applications are served behind a proxy router.\n' +
'To do so, you need to first register the custom application in Merchant Center > Settings > Custom Applications.'
);
};

This route will be used as a serverless function:

./config/transformer-vercel.jsJavaScript
const fs = require('fs');
const path = require('path');
const rootPath = path.join(__dirname, '..');
module.exports = function transformer({ headers }) {
const config = {
version: 2,
public: true,
name: 'mc-examples-starter.vercel.app',
alias: 'mc-examples-starter.vercel.app',
regions: ['bru'],
builds: [
{ src: 'public/**', use: '@vercel/static' },
{ src: 'config/fallback-route.js', use: '@vercel/node' },
],
routes: [
{
src: '/(.*).(js.map|js|css|txt|html|png)',
dest: '/public/$1.$2',
headers: { 'Cache-Control': 's-maxage=31536000,immutable' },
},
{ src: '/(login|logout)', dest: '/config/fallback-route.js' },
{
src: '/(.*)',
dest: '/public/index.html',
headers: { 'Cache-Control': 'no-cache', ...headers },
},
],
};
fs.writeFileSync(
path.join(rootPath, 'vercel.json'),
JSON.stringify(config, null, 2),
{ encoding: 'utf8' }
);
};

Deployment

Finally, we can trigger the deployment using the Vercel CLI:

yarn build
mc-scripts compile-html \
--transformer ./config/transformer-vercel.js
vercel

Now you're ready to Register your Custom Application and start using it!