The Vue Storefront Essentials Course is now available! Learn More
Data Integration and Orchestration

Data Integration and Orchestration

Optimizing server requests through data integration and orchestration is essential for frontend performance. This guide introduces the orchestration feature, highlighting its role in integrating extensions and streamlining frontend code.

Enhancing Frontend Performance through Data Orchestration

Data orchestration allows for consolidating multiple server requests into a single endpoint, which significantly eases the burden on the frontend. This is particularly beneficial in scenarios involving numerous simultaneous server requests.

Advantages:

  • Minimized Network Traffic: Fewer server calls lead to reduced latency and enhanced responsiveness.
  • Simplified Frontend Architecture: By grouping related server requests, the frontend logic becomes less complex.
  • Uniform Data Collection: Ensures that data fetched from different sources is consistent and provided in a standard format.

Implementation:

The getApiClient Method

If you want to retrieve a loaded integration within the context of another, you can use the getApiClient method. This method serves as a bridge between various integrations, ensuring seamless data flow and interaction.

Usage:

const sapcc = context.getApiClient("sapcc");

The getApiClient method takes a single argument, which is the key of the api client you wish to retrieve. This is the key you would define in the middleware.config.js file for the integration you wish to retrieve. The key is essentially an identifier for the integration.

Here's a basic example of what this might look like:

export const integrations = {
  sapcc: {
    location: "@vsf-enterprise/sapcc-api/server",
    configuration: {
      // ...
    },
    extensions: (extensions) => [
      ...extensions,
      {
        name: "sapcc-contentful-extension",
        extendApiMethods: {
          getPDP: async (context, params: { id: string }) => {
            const sapcc = context.getApiClient("sapcc");
            const contentful = context.getApiClient("contentful");

            const [product, content] = Promise.all(
              sapcc.api.getProduct({ id: params.id }),
              contentful.api.getEntries({
                content_type: "product",
                "fields.sku": params.id,
              })
            );

            return {
              product,
              content,
            };
          },
        },
      },
    ],
  },
  contentful: {
    location: "@vsf-enterprise/contentful-api/server",
    configuration: {
      // ...
    },
  },
};
  1. Extend the integration with new endpoint: Create a new endpoint that will act as the main entry point for the grouped requests.
  2. Group Server Requests: Within this endpoint, utilize the getApiClient method to retrieve and interact with the required integrations.
  3. Aggregate Data: Once data from all required integrations is retrieved, aggregate and format it as needed.
  4. Return Unified Response: Send a consolidated response back to the frontend.:

Using orchestration methods in the frontend

To call the orchestration endpoint, you need to send a POST request to the endpoint URL. In our example it would be POST http://localhost:8080/sapcc/getPDP. The request body should contain the parameters required by the endpoint.

When it comes to extending the Vue Storefront integrations like sapcc or contentful, you can use the SDK extension mechanism as described in the Extending a Module guide.

Real-World Examples

The examples provided demonstrate practical uses of data orchestration:

Example 1: Fetching Custom Product Properties from Legacy Systems

This use case involves calling the commerce backend to fetch specific product data. Additionally, a separate call is made to a legacy custom system of the customer, to retrieve a custom product property (e.g., stock of the product). This data is used, for example, to display stock information on the product page.

Example implementation might look like this:

middleware.config.ts
export const integrations = {
  sapcc: {
    // ...
    extensions: (extensions) => [
      ...extensions,
      {
        name: "orchestration-extension",
        extendApiMethods: {
          enrichedSearch: async (context, params: { productId: string }) => {
            const sapcc = context.getApiClient("sapcc");
            const legacyCustomSystem = context.getApiClient("legacyCustomSystem");

            const prouctStock = await legacyCustomSystem.api.getProductStock({
              productId: params.productId,
            });

            const product = await sapcc.api.getProduct({
              { id: params.productId },
            });

            return {
              ...product,
              stock: productStock,
            };
          },
        },
      },
    ],
  },
  legacyCustomSystem: {
    // ...
  },
};

Example 2: Product Slider Orchestration with Commerce Backend Data

In this scenario, the CMS returns a product slider component that lacks sufficient product data (or contains only product IDs). The orchestration layer enhances the product slider by adding product data from the commerce backend, ensuring a comprehensive and informative display.

middleware.config.ts
export const integrations = {
  sapcc: {
    // ...
    extensions: (extensions) => [
      ...extensions,
      {
        name: "orchestration-extension",
        extendApiMethods: {
          getProductSlider: async (
            context: IntegrationContext,
            props: { entryId: string }
          ) => {
            const contentful: ApiClient<ContentfulEndpoints> =
              context.getApiClient("contentful");
            const sapcc: ApiClient<SapccEndpoints> =
              context.getApiClient("sapcc");

            const content = await contentful.api.getEntry(props.entryId);
            const productItems = await Promise.all(
              content.fields.items.map(async (item) => {
                const product = await sapcc.api.getProduct({
                  id: item.fields.productId,
                });

                return {
                  ...item,
                  fields: {
                    ...item.fields,
                    ...product,
                  },
                };
              })
            );

            return {
              ...content,
              fields: {
                ...content.fields,
                items: productItems,
              },
            };
          },
        },
      },
    ],
  },
  contentful: {
    // ...
  },
};

TypeScript Support

getApiClient helper returns the ApiClient interface, which is a generic type. It takes three type arguments:

  • Api - the type of the API object returned by the integration,
  • Config - the type of the configuration object passed to the integration,
  • Client - the type of the HTTP client object returned by the integration.

Usually, an integration exports those types. For example, the sapcc integration exports the following types:

import {
  Endpoints,
  MiddlewareConfig,
  AxiosInstance,
} from "@vsf-enterprise/sapcc-api";

Endpoints and the context object

Each method of the integration api client contains the context object as the first argument. However, the context is not something that is passed by the developer to the method during the call. This is because the context is passed automatically by the @vue-storefront/middleware package logic. Because of this, the context object should be excluded from the Api interface passed to the ApiClient type.

To achieve that, the @vue-storefront/middleware export the interface ContextualizedApi, which basically removes the context from the API.

Example

Let's take a look at the sapcc integration. The sapcc integration exports the following types:

import {
  Endpoints,
  MiddlewareConfig,
  AxiosInstance,
} from "@vsf-enterprise/sapcc-api";
import { ContextualizedApi } from "@vue-storefront/middleware";

// ...

const sapcc = context.getApiClient<
  ContextualizedApi<Endpoints>,
  MiddlewareConfig,
  AxiosInstance
>("sapcc");

// sapcc.api now is aware of the SAPCC methods that are not expecting the `context` object
// sapcc.config is now aware of the SAPCC configuration object
// sapcc.client is now aware of the SAPCC HTTP client object

Type of endpoints

Sometimes, the Endpoints type is not exported by the integration. If that's the case, you can import the XyzIntegrationContext type from the integration package. For example, the sapcc integration exports the SapccIntegrationContext type, which contains the endpoints type as SapccIntegrationContext['api']. It contains also the type of the configuration object as SapccIntegrationContext['config'] and the type of the HTTP client object as SapccIntegrationContext['client']. This should be applied to all integrations.