Connect your data
Cube Cloud

Connecting Embeddable to Cube Cloud

Embeddable works seamlessly with Cube Cloud (opens in a new tab), letting you pick and visualize your Cube models (opens in a new tab) right in Embeddable’s no-code builder.

To connect Embeddable to Cube you use the Data Provider API.

Data Provider API

To connect Cube Cloud, you’ll need to provide your REST API Endpoint and API Secret to Embeddable.

Find Your Cube Credentials

  1. REST API Endpoint

    • You can find your endpoint in the Integrations tab in the Cube Cloud platform, accessible from the left side menu. Once you’re there, select the tab REST API and copy your API credentials under Endpoint.
    Image 0
  2. API Secret

    • Under SettingsEnvironment Variables in Cube Cloud, look for CUBEJS_API_Secret.
    Image 0

Create a Data Provider

Connect Embeddable to your Cube deployment using our Data Provider API, which also allows you to manage single/multiple deployments efficiently.

You can call the Data Provider API directly using tools like Postman (opens in a new tab) or Bruno (link below)

Open APIs In Bruno

The example below shows a CREATE action. Other CRUD operations (list, read, update, delete) are available in our Bruno APIs collection.

fetch(`https://api.<region>.embeddable.com/api/v1/data-providers`, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${apiKey}` /* keep your API Key secure */,
    },
    body: JSON.stringify({
        name: 'main-instance', // unique name for your cube endpoint
        type: 'cube', // Data provider type
        credentials: {
          secretKey: '<your API Secret>',
          instanceUrl: '<your REST API Endpoint>'
        },
    }),
});
  • apiKey: This is located on the homepage of your workspace.
  • name: A unique identifier you'll use to reference your Cube deployment (e.g. main-instance)
  • type: Always set to cube for external cube deployments.
  • credentials:
    • secretKey – your API secret.
    • instanceUrl – your REST API Endpoint.
  • region: Learn more about region here.

For example, if we set name = "main-instance" when connecting to Cube using the Data Provider API, we'll need to update our security context file like this:

- name: Example customer 1
  securityContext:
    country: United States
    userid: 123
  dataProvider: main-instance 

Use Your Cube Models in Embeddable

Watch this short video demonstrating Cube Cloud and Embeddable in action:

Things to notice:

  • [0:00] Your cube models, dimensions and measures all appear in Embeddable via Datasets.
  • [0:57] If you make changes to your models, just click the Refresh button in the Datasets section to bring in your changes.
  • [1:35] You can switch between security contexts using the View As dropdown.

Notes and best practices

  • The dataProvider you add in your security context file or Tokens API request must match the data provider you created using the Data Providers API.
  • Use clear names for your data providers (e.g., main-instance) so they map directly to your Cube instances.
  • Anything you want to pass from Embeddable to your Cube deployment, ensure it is inside the securityContext.

Testing Multiple Data Providers

Once you've connected your Cube deployment to Embeddable as explained above, you might want to connect multiple Cube deployments. This approach helps you manage different environments, test changes safely, and support various business needs.

Embeddable supports connecting to multiple cube deployments through the Data Provider API. These deployments generally fall into two categories:

  1. Preview or Staging Branches
    • These are separate staging URLs within the same deployment.
    • They allow you to test changes in Embeddable before merging them into your main branch.
    • Example: Creating a branch to experiment with a new measure or model update without impacting production.
  2. Completely Separate Deployments
    • These are fully independent deployments, often used to handle different workloads, serve separate lines of business, or scale horizontally.
    • Example: One deployment for internal analytics and another for external customer-facing analytics.

Preview or Staging Branches

Lets walkthrough an example of testing a new measure in Embeddable before we merge to our main branch.

Firstly, we'll create a new branch called add_completed_percentage_measure. In this branch, we'll add a new "completed percentage" measure to our base_orders model and include it in the orders view.

Next, we’ll create a new data provider called testing that points to a specific staging environment (opens in a new tab).

Each staging branch in Cube Cloud has its own unique staging URL, which you’ll need to configure this data provider.

To set it up, you’ll need two things:

  1. Staging URL – Go to Settings → Staging Environments to find the URL for your branch.
  2. Secret Key – Go to Settings → Environment Variables → CUBEJS_API_SECRET to find the key.

Once you have both, pass them to the Data Provider API when creating the testing data provider.

Now let's update our security context file to include these two security contexts:

Example 1: Main Instance

- name: Main Instance
  securityContext:
    country: United States
    userid: 123
  dataProvider: main-instance 

Example 2: Testing

- name: Testing
  securityContext:
    country: United States
    userid: 123
  dataProvider: testing #points to staging environment

Switch between different security contexts in the no-code builder to test how data providers affect visibility.

Things to notice

  • dataProviders are configured to your main instance and testing.
  • The main instance do not have the completed_percentage measure.
  • completed_percentage measure is only available in the testing data provider that points to your staging environment url.
  • You can test this new measure in data playground or dashboard before merging it to your main branch.

Completely Separate Deployments

For separate deployments, you'll need to set up a data provider for each one. Since each deployment has its own Rest API Endpoint and Secret, just repeat the same configuration steps for each deployment as described earlier here.

Cube Cloud Dual Authentication (JWKS)

This section explains how to configure dual authentication for Cube Cloud, allowing your Cube Cloud instance to support both JWK-based authentication (for customer applications) and secret-based authentication (for the Embeddable backend).

Cube Cloud Configuration

  • In your Cube Cloud environment, set the following environment variables:
    • CUBEJS_JWK_URL: Set this to the JWKS endpoint URL from your identity provider. For example, Auth0 provides the following URL: https://{yourDomain}/.well-known/jwks.json

Cube.js Configuration Files

  • Copy the auth.js file to the root of the cube cloud folder (see below for example).
  • Copy the cube.js file to the root of the cube cloud folder (see below for example).
  • Copy the package.json file to the root of the cube cloud folder. This file lists the required dependencies (jsonwebtoken and jwks-rsa - see below for example).

Deploy to Cube Cloud

How it Works

  • JWK Authentication: When a request comes from your applications, Cube Cloud will use the CUBEJS_JWK_URL and the validateJwt function in auth.js to authenticate the request using the provided JWT and the JWKS from your identity provider.
  • Secret-Based Authentication: When a request comes from the Embeddable backend, it will include the Request-Origin: embeddable header. Cube Cloud will then use the CUBEJS_API_SECRET and the validateEmbeddableJwt function in auth.js to authenticate the request using the shared secret key.

Important Notes

  • Backend Requests: The Embeddable backend will automatically include the Request-Origin header in its requests to Cube Cloud.

This dual authentication setup provides flexibility and security for your Cube.js integration, allowing you to use both JWK-based and secret-based authentication methods as needed.

Auth.js

This is an example auth.js file. Copying and pasting this file probably won’t work for you (unless you have identical environment variable names), but it’s a good way to get started!

const jwt = require("jsonwebtoken");
const jwksRsa = require('jwks-rsa'); // jwks-rsa library handles caching of the JWKS internally
 
class AuthHandlerError extends Error {
  constructor(status, message) {
    super(message);
    this.status = status;
  }
}
 
const jwkUrl = process.env.CUBEJS_JWK_URL;
const jwksClient = jwksRsa({
  jwksUri: jwkUrl
});
const secretKey = process.env.CUBEJS_API_SECRET;
 
const customCheckAuth = async (req, auth) => {
  if (auth) {
    if (req.get('Request-Origin') === 'embeddable') {
      validateEmbeddableJwt(auth, req); // Secret key authentication
      console.debug("validateEmbeddableJwt JWT verification successful.")
    } else {
      await validateJwt(auth, req); // JWK authentication
      console.debug("validateJwt JWT verification successful.")
    }
  } else {
    throw new AuthHandlerError(403, "Authentication required");
  }
};
 
const validateEmbeddableJwt = (auth, req) => {
  try {
    const decoded = jwt.verify(auth, secretKey, {algorithms: ['HS256']});
 
    req.securityContext = decoded;
  } catch (err) {
    throw new AuthHandlerError(403, `validateEmbeddableJwt JWT validation failed: ${err.message}, ${auth}`);
  }
};
 
const validateJwt = async (auth, req) => {
  try {
    console.log("Starting JWT validation...");
 
    const getKey = (header, callback) => {
      console.debug("Fetching signing key with kid:", header.kid);
      jwksClient.getSigningKey(header.kid, (err, key) => {
        if (err) {
          console.error("Error getting signing key:", err);
          callback(err, null);
          return;
        }
        console.debug("Signing key:", key);
        const signingKey = key.getPublicKey();
        callback(null, signingKey);
      });
    };
 
    const decoded = await new Promise((resolve, reject) => {
      jwt.verify(auth, getKey, {algorithms: ["RS256"]}, (err, decoded) => {
        if (err) {
          reject(new AuthHandlerError(403, `jwt.verify, JWT validation failed: ${err.message}`));
        } else {
          resolve(decoded);
        }
      });
    });
 
    console.debug("JWT verification successful. Decoded payload:", decoded);
    req.securityContext = decoded;
 
  } catch (err) {
    throw new AuthHandlerError(403, `validateJwt JWT, validation failed: ${err.message}`);
  }
};
 
module.exports = {customCheckAuth, AuthHandlerError};

Cube.js

This is an example cube.js file. Copying and pasting this file probably will work for you, although you may want to modify it!

const { customCheckAuth } = require('./auth');
 
module.exports = {
  checkAuth: async (req, auth) => {
    await customCheckAuth(req, auth);
  }
}

Package.json

This is an example package.json file. This is almost certainly not everything you’ll need in your version. Better to just add these items to your dependencies object!

{
  "dependencies": {
    "jsonwebtoken": "^9.0.2",
    "jwks-rsa": "^3.1.0"
  }
}