Creating a Custom Integration
If you're looking to create a custom integration for Alokai, you're in the right place. This guide will walk you through the process of creating a custom integration from scratch.
Generating integration boilerplate is supported since @alokai/cli@v1.1.0
.
To help you scaffold a project, you can use our CLI to generate the boilerplate for your integration. To do that, run the following command:
yarn alokai-cli integration generate <integration-name>
You can check the examples and flags with their descriptions by running yarn alokai-cli integration generate --help
.
Code structure
The command generates code in two main locations:
apps/storefront-middleware/integrations/<integration-name>
- Contains the code for the Alokai Middleware.apps/storefront-unified-<framework>/integrations/<integration-name>
- Contains the SDK module that will be used in the Storefront.
Alokai Middleware
An integration used in the Alokai Middleware serves as a bridge between the Storefront and the 3rd party service (like commerce backend).
The structure of the Alokai Middleware integration is the following:
apps/storefront-middleware/integrations/<integration-name>/
├── ai/ # Instructions for the AI agents
├── api/ # API endpoints
├── client/ # HTTP Client implementation
├── extensions/ # Extensions for the integration
│ └── unified/ # Unified extension
│ ├── api/ # Unified API method implementations
│ ├── commons/ # Shared utilities
│ ├── normalizers/ # Unified data normalizers
│ └── types/ # Unified type definitions
├── types/ # Integration-specific types
├── config.ts # Config for the Alokai Middleware
├── index.server.ts # Entry point of the integration
└── index.ts # Exports the config and unified extension
SDK Module
The command generates two SDK modules that will be used to communicate with the integration registered in the Alokai Middleware and its unified extension.
The structure of the SDK modules is the following:
apps/storefront-unified-<framework>/sdk/modules/<integration-name>/
├── <integration-name>.ts # SDK module to communicate with the integration's raw API
└── unified-<integration-name>.ts # SDK module to communicate with the integration's unified extension
How to use the integration
1
First, you need to register the integration in the Alokai Middleware.
+ import { config as <integration-name>Config } from './integrations/<integration-name>';
export const config = {
integrations: {
+ <integration-name>: <integration-name>Config,
},
};
2
Then, export the SDK modules from the sdk/modules/index.ts
file.
+ export * from './<integration-name>';
+ export * from './unified-<integration-name>';
3
Now, you can use the integration in the Storefront.
const sdk = await getSdk();
await sdk.<integration-name>.exampleMethod({ id: '1' });
await sdk.unified<integration-name>.getCart({ cartId: '1' });
AI support
Integration boilerplate ships with instructions for AI code editors (e.g., Windsurf or Cursor). They will help the editor's agent to:
- implement an HTTP client connecting the boilerplate with the desired 3rd party platform (e.g., commerce or CMS),
- implement API methods fetching non-unified data from the 3rd party platform (e.g., product data),
- implement Unified API methods fetching non-unified data via an existing API method and normalizing it to the unified format with a normalizer.
For the instructions to work, the content of the /ai/general-rules.md
file has to be added to the main AI context file (e.g., .windsurfrules
or .cursorrules
).
Raw API, Unified API, and Normalizers
The integration consists of three main components that work together to provide a seamless experience:
- Raw API Methods - These methods communicate directly with the 3rd party service (e.g., commerce backend) and return data in its native format.
- Unified API Methods - These methods use raw API methods and normalizers to transform the data into a unified format that can be consumed by the Storefront. Here's an example:
import type { SfProduct } from '@alokai/connect';
import { getNormalizers } from '@alokai/connect/integration-kit';
import { defineApi } from '../../commons';
export const getProducts = defineApi.getProducts(async (context) => {
const { normalizeProductCatalogItem } = getNormalizers(context);
// Call the raw API method to fetch data from the 3rd party service
const result = await context.api.exampleMethod({ id: '1' });
// Normalize the data to the unified format
const rawProducts: SfProduct[] = result.data;
const products = rawProducts.map(normalizeProductCatalogItem);
// Return the normalized data to the Storefront
return { products };
});
- Normalizers - These are functions that transform data from the 3rd party service's format into the unified format expected by the Storefront. They are used within unified API methods to ensure consistent data structure across different integrations.
The flow of data is as follows:
- The Storefront calls a unified API method
- The unified API method calls a raw API method to fetch data from the 3rd party service
- The raw data is then normalized using normalizers
- The normalized data is returned to the Storefront
This architecture ensures that the Storefront always receives data in a consistent format, regardless of the underlying 3rd party service.
Example getProducts
method
To illustrate the data flow, let's examine the getProducts
method. We'll use Fake Store API as our example commerce backend.
1
Create a new file at my-integration/api/getProducts/index.ts
with the following content:
import { IntegrationContext } from '../../types/context';
export interface FakeStoreProduct {
id: number;
title: string;
price: number;
description: string;
category: string;
image: string;
rating: {
rate: number;
count: number;
};
}
/**
* Fetches products from the Fake Store API
*
* @param context - The integration context containing the HTTP client
* @returns Promise containing an array of products with their details
*/
export async function getProducts(context: IntegrationContext): Promise<FakeStoreProduct[]> {
const response = await context.client('https://fakestoreapi.com/products');
const data = await response.json();
return data as FakeStoreProduct[];
}
2
Export the method in my-integration/api/index.ts
:
export * from './getProducts';
3
Implement the Product Catalog Normalizer
Open my-integration/extensions/unified/normalizers/product/catalog.ts
and import the FakeStoreProduct
type:
import type { FakeStoreProduct } from '../../../../api/getProducts';
Then, update the normalizer to handle Fake Store data:
export const normalizeProductCatalogItem = defineNormalizer.normalizeProductCatalogItem(
(context, rawProduct: FakeStoreProduct) => {
const primaryImage = context.normalizers.normalizeImage({
url: rawProduct.image,
alt: rawProduct.title,
});
const price = context.normalizers.normalizeDiscountablePrice({
value: rawProduct.price,
currency: 'USD',
});
const rating = context.normalizers.normalizeRating({
value: rawProduct.rating.rate,
count: rawProduct.rating.count,
});
return {
id: rawProduct.id.toString(),
name: rawProduct.title,
price: price,
primaryImage: primaryImage,
quantityLimit: 100,
rating: rating,
sku: `FS-${rawProduct.id}`,
slug: rawProduct.title.toLowerCase().replace(/\s+/g, '-'),
};
},
);
4
Implement the Unified API Method
Open my-integration/extensions/unified/api/product/getProducts.ts
and import the necessary types and utilities:
import type { SfProduct } from '@alokai/connect';
import { getNormalizers } from '@alokai/connect/integration-kit';
import { defineApi } from '../../commons';
Finally, implement the unified getProducts
method:
export const getProducts = defineApi.getProducts(async (context) => {
const { normalizeProductCatalogItem } = getNormalizers(context);
const rawProducts = await context.api.getProducts();
const products = rawProducts.map(normalizeProductCatalogItem);
return { products };
});
5
Test the Integration
To test the integration, call the getProducts
and unified getProducts
methods in the Storefront:
const sdk = await getSdk();
const products = await sdk.unified.getProducts();
console.log(products);
const rawProducts = await sdk.getProducts();
console.log(rawProducts);
The method should return the products in the unified format.