API hub configuration

In this guide, you'll learn to set environment variables in the API hub and access them in your extension handlers.

Configuring variables in API hub

You can provide your API hub extensions with credentials to connect to your backend services. To do so, you need to edit the project.yml file in a directory you're not usually meant to touch: <customer>_<project>/config/project.yml. In this file, you can edit the section configuration and store arbitrary YAML content, which is then available to the FrontasticContext in each extension point.

apiKey:
configuration:
# ...
commerce-backend:
apiKey: <perfect-api-key-secrets>
serverKey: <secret-server-key>

You'll break your API hub setup if you edit any other section in this file.

Accessing variables in extension handlers

You can access the specified variables through the context property that's passed in as the second argument to your extension handlers. See the examples below to learn more.

export default {
'dynamic-page-handler': async (
request: Request,
context: DynamicPageContext
): Promise<DynamicPageSuccessResult | DynamicPageRedirectResult | null> => {
const { apiKey } =
context.frontasticContext.project.configuration['commerce-backend'];
// fetch data for dynamic pages
},
'data-sources': {
'amazing-data-source': async (
config: DataSourceConfiguration,
context: DataSourceContext
): Promise<DataSourceResult> => {
const { serverKey } =
context.frontasticContext.project.configuration['commerce-backend'];
// fetch data with the server key
},
},
actions: {
commerce: {
getStores: async (
request: Request,
context: ActionContext
): Promise<Response> => {
const { serverKey } =
context.frontasticContext.project.configuration['commerce-backend'];
// fetch stores with the server key
},
},
},
};

Accessing secrets in frontend components

Sometimes you'd need the variables in the frontend components. For example, some client-side SDKs like Adyen need a client id to work. The API hub doesn't expose the variables directly to the frontend, but you can easily implement a data source to selectively expose the variables you need in a component.

The following example shows how you can expose the adyen.clientID secret from the project.yml to a frontend component.

# other details...
configuration:
adyen:
clientID: top_secret_client_id
# more configurations ...
  1. Create a data source to read the variable from project.yml and return the variable.
export default {
// ... other extensions
'data-sources': {
'secrets/adyen-client-id': async (
config: DataSourceConfiguration,
context: DataSourceContext
) => {
return {
dataSourcePayload:
context.frontasticContext.project.configuration.adyen.clientID,
};
},
},
// ... other extensions
};
  1. Upload the data source schema to the studio.
{
"customDataSourceType": "secrets/adyen-client-id",
"name": "Expose Adyen Client ID",
"category": "Secrets",
"icon": "key",
"schema": []
}
  1. Specify the data source in the Frontend component's schema that needs the Adyen client ID.
{
"tasticType": "example/adyen-payment",
"name": "Example payment",
"icon": "money",
"category": "payment",
"schema": [
{
"name": "Secrets",
"fields": [
{
"label": "Adyen client id",
"field": "adyenClientId",
"type": "dataSource",
"dataSourceType": "secrets/adyen-client-id",
"required": true
}
]
}
]
}
  1. Use the adyenClientId in the Frontend component.
function ExamplePayment({ data }) {
return (
<h2>
I got this secret from data source{' '}
<b>{data?.adyenClientId?.dataSource}</b>
</h2>
);
}
export default ExamplePayment;

This implementation keeps your application secrets safe by allowing you to granularly expose secrets to the components.

It's essential to expose as little as possible to avoid security issues. A good practice is creating separate data sources for each secret needed on the frontend.

Sourcing hierarchy

The files are sourced in the following order and values are overwritten by later files:

Production

  • project.yml

Staging

  • project.yml
  • project.yml.staging

Development

  • project.yml
  • project.yml.dev

So, if you have the same key-value pair specified in both project.yml and project.yml.dev the .dev one will be used.

Securing your project secrets

We've introduced a way to add another ansible-vault for encrypted project configurations. This vault should be just an encrypted version of the project.yml with the necessary overwrites and should be named accordingly:

  • project.yml.crypt for Production project.yml
  • project.yml.staging.crypt for Staging project.yml.staging

These files will then get decrypted on our servers to make them available there.

Creating the Ansible-Vault

In order to create a vault, you could follow the documentation of Ansible.

Basically, you'll need to run the following command on your shell inside the project's config directory:

# cd to project's config directory, where $PROJECT is something like "demo_de"
cd $PROJECT/config
# create the ansible vault there with an vault-id that equals your customer's name, for example, "demo"
# Keep in mind that it should be a unique password that needs to be known by us as well, so please don't reuse a password here
ansible-vault create --vault-id $CUSTOMER@prompt project.yml.crypt

We need to know your password to configure our servers properly. Don't reuse another password here!

Choose any new password you like and contact us using the Submit a ticket button (at the top of this page) so we can configure the servers properly to use the password you've used so that it gets decrypted on the production and staging machines.

Please don't forget to check the generated encrypted vault file in Git.

Editing the Ansible-Vault

You could edit the vault by running the following command:

ansible-vault edit --vault-id $CUSTOMER@prompt project.yml.crypt

After editing and once the changes are pushed into your master branch, please contact our Support team (using the Submit a ticket button) who will make sure these are applied to your production and/or staging servers.

For further details, see the Ansible documentation.