SDK layer

Learn about the improvements to the commercetools Frontend SDK and how they can improve your frontend development.

  • After completing this page, you should be able to:

    • Identify the key changes to the Frontend SDK in the past year.
  • With the newly available Frontend SDK, you can now develop components and integrations with ease. It introduces a new layer to have your integrations backend-agnostic and stable under new developments. Let's use it and have a look.

    Installation

    Using the following commands, you can gain access to both the base Frontend SDK and our domain types:

    yarn add @commercetools/frontend-sdk
    yarn add @commercetools/frontend-domain-types

    Singleton pattern behavior

    When adding a new layer of configurations and creating custom events for your needs, you might easily run into a pattern where you make too many calls and end up having multiple instances for the same event. This pattern decreases the performance of your application, and to address this, we've adopted the Singleton pattern behavior for our SDK and integration packages.

    Check the following example:

    import { SDK } from '@commercetools/frontend-sdk';
    import {
    ComposableCommerce,
    ComposableCommerceEvents,
    } from '@commercetools/frontend-composable-commerce';
    // Add other integration's custom events to the SDK's generic type here,
    // by extending ComposableCommerceEvents with their type using an intersection.
    // For example, <ComposableCommerceEvents & OtherEvents>.
    // You may also add your own custom events.
    class CommercetoolsSDK extends SDK<ComposableCommerceEvents> {
    composableCommerce!: ComposableCommerce;
    // Add your other integrations here.
    constructor() {
    super();
    this.composableCommerce = new ComposableCommerce(this);
    // Initialize your other integrations here.
    this.on('errorCaught', (event) => {
    // Handle any integration errors caught by the SDK globally.
    // For example, log error, fire notification, etc.
    console.log('SDK error: ', event.data);
    });
    // Set up any other custom global event handlers here.
    // Ensure types are created and added to the SDK generic type
    // if specific to your project.
    }
    }
    // Create a single instance of the SDK.
    const sdk = new CommercetoolsSDK();
    // Export only the instance to serve as a singleton throughout the project.
    export { sdk };

    Configuration

    You can use the Frontend SDK with or without integrations with the Composable Commerce APIs.

    Configuration with integration

    The best way to make the most of our SDK is to use it in sync with Composable Commerce. You can call the integration from the packages/<project>/sdk path.

    import { NextPage } from 'next';
    import React, { useEffect, useState } from 'react';
    import App from '../../components/App';
    import HorizontalSpacer from '../../components/horizontalSpacer';
    import { sdk } from '../../sdk';
    import Button from 'src/components/form/button';
    import { ProjectSettings } from '@commercetools/frontend-domain-types/ProjectSettings';
    const ExamplePage: NextPage = () => {
    const [projectSettings, setProjectSettings] = useState<ProjectSettings>();
    const [loading, setLoading] = useState(false);
    const projectSettingsReturnedEvent = (
    event: Event<
    'projectSettingsReturned',
    {
    settings: unknown;
    }
    >
    ) => {
    // Access the commercetools Frontend project settings here.
    console.log('Event projectSettingsReturned fired: ', event.data.settings);
    };
    useEffect(() => {
    sdk.on('projectSettingsReturned', projectSettingsReturnedEvent);
    return () => {
    sdk.off('projectSettingsReturned', projectSettingsReturnedEvent);
    };
    }, []);
    const getSettings = async () => {
    setLoading(true);
    const response = await sdk.composableCommerce.project.getSettings();
    setLoading(false);
    if (response.isError === false) {
    setProjectSettings(response.data);
    }
    };
    const style = { fontWeight: 'bold', textDecoration: 'underline' };
    return (
    <App>
    <>
    <h1>Example</h1>
    <HorizontalSpacer />
    <Button
    text="TEST"
    onSubmit={
    projectSettings ? () => setProjectSettings(undefined) : getSettings
    }
    loading={loading}
    />
    <HorizontalSpacer />
    {projectSettings && (
    <>
    <>
    <h2 style={style}>NAME</h2>
    <p>{projectSettings.name}</p>
    </>
    </>
    )}
    </>
    </App>
    );
    };
    export default ExamplePage;

    For more information, see Configure the SDK.

    For strict projects, a simple truthy/falsy comparison such as !response.isError is sufficient. For other cases, compare the return value of getSettings with a boolean value, otherwise when accessing response.data, the following error may occur: Property data does not exist on type.

    Configuration without integration

    If you use your own backend or your main focus is only the frontend, you will see everything you need to know about using the SDK without integration.

    Next.js project

    Since we are dealing with a Singleton pattern, we need to import and call the SDK only once. It will then be available throughout the application, on any page. Check the following implementation inside a next.js project.

    import { AppProps } from 'next/app';
    import { sdk } from '../sdk';
    sdk.configure({
    locale: 'de-DE',
    currency: 'EUR',
    endpoint: 'EXTENSION_RUNNER_HOSTNAME',
    });
    const Starter = ({ Component, pageProps }: AppProps) => {
    return <Component {...pageProps} />;
    };
    export default Starter;

    For more information, see Configure the SDK.

    Server-side rendering

    For server-side rendering, you can import the configuration properties from the .env, .config file, or getServerProps call.

    import { GetServerSideProps, Redirect } from 'next';
    import { mapSDKLanguage } from 'project.config';
    import { sdk } from '../sdk';
    type SlugProps = {
    ...
    };
    export default function Slug({ data }: SlugProps) {
    ...
    }
    export const getServerSideProps: GetServerSideProps | Redirect = async ({ params, locale, query, req, res }) => {
    sdk.configure({
    locale: mapSDKLanguage(locale),
    currency: 'EUR',
    endpoint: (process.env.NEXT_PUBLIC_FRONTASTIC_HOST ?? ”https://my.staging.server/frontastic”).split('/frontastic')[0],
    });
    ...
    };

    SDK extension

    Out-of-the-box integration

    We have created an out-of-the-box template to provide you the best way to get started with the SDK. Let's look at the following topics to work with this template:

    CommercetoolsSDK class and ComposableCommerceEvents type

    • The CommercetoolsSDK class extends the SDK class from @commercetools/frontend-sdk, which provides the CommercetoolsSDK class with the methods and properties available in the SDK.
    • The ComposableCommerceEvents type is passed to the generic SDK argument. It comes from the @commercetools/frontend-composable-commerce integration package and specifies the types of any custom events of this particular package.

    For more information about events, see CommercetoolsSDK class and ComposableCommerceEvents type.

    The following is an example of a pattern for extending the events:

    export type Events = {
    [key: string]: {
    [key: string]: unkown,
    },
    };

    And an implementation example:

    export type CustomEventTriggers = {
    someEventTriggered: { time: string },
    someComponentClicked: { x: number, y: number },
    };

    SDK constructor

    Two major processes occur inside the constructor: initialization of @commercetools/frontend-composable-commerce integration and default assignment of error handler for errorCaught event. For more information about the constructor, see CommercetoolsSDK.ts constructor.

    Instance of CommercetoolsSDK class

    Following the Singleton pattern, we need to export only the instance of the class and not the class itself. If the SDK is imported in multiple places, we will use the same instance of the class and not more. This also affects how many event handlers are constructed and added.

    Add integrations to the SDK

    To add an integration, follow these steps:

    1. Set the integration in the constructor.
    2. Pass the SDK instance as ComposableCommerce instance.
    3. Add existing custom events as an intersection as a generic argument for the SDK.
    4. Add backend actions.

    To learn how to add a custom event using intersection, follow this example.

    Custom event triggers

    To create a custom event trigger (click or hover over a component) you need to follow the following steps:

    1. Create a new file inside the sdk folder.
    2. Import the file to the CommercetoolsSDK class as a generic SDK argument, as an intersection.
    3. Clean up event handlers set inside the component to avoid remounting the components and incorrect state updates.

    To learn how to create custom event triggers, follow this example.

    Integration Development

    You might remember that there are two types of integrations: backend extensions (data sources, dynamic page handlers and actions) and frontend SDK integrations.

    To recap how to work with backend extensions, see Extension. Follow along to do it now with the SDK. Let's start by adding the required dependencies.

    yarn add @commercetools/frontend-sdk -D
    yarn add @commercetools/frontend-sdk -P
    yarn add @commercetools/frontend-domain-types

    We recommend the following settings for your project:

    {
    "compilerOptions": {
    "target": "ES2022",
    "strict": true,
    "keyofStringsOnly": true,
    "allowSyntheticDefaultImports": true,
    "types": ["node"],
    "declaration": true,
    "outDir": "lib",
    "moduleResolution": "node"
    },
    "exclude": ["node_modules", "lib"]
    }

    Now that the project is set up and configured, the most important part comes into play—defining your own methods and types:

    • The key export of an integration is a class that extends the Integration abstract class exported from @commercetools/frontend-sdk. Your integration must take the SDK singleton in the constructor and store the instance as a property.
    • The Integration abstract class also defines the CustomEvents generic type to extend the @commercetools/frontend-sdk StandardEvents type, which extends the Events type.

    For more information about integration implementation, see this example.

    You can then make use of the Frontend SDK event management tools. This is made possible due to the usage of EventManager class. Three methods exist in the SDK for the management of event handlers:

    • trigger: called to trigger an event for which an instance of the Event class from @comercetools/frontend-sdk is passed.
    • on: called to add an event handler for an event. The method takes the eventName and handler arguments.
    • off: called to remove an event handler. For example, to persist the handler only for the lifecycle of a particular component.

    You can see an example of event triggering in action here.

    Test your knowledge