Utilising SDK modules
Introduction
Alokai SDK is a framework-agnostic communication layer in Alokai Integrations. It communicates with Commerce Platforms and third-party services through the Server Middleware, which works as a proxy. Alokai SDK creates a contract between the storefront and the Server Middleware.
The middlewareModule
is a module of the Alokai SDK that allows you to interact with the Storefront API. It is designed to work seamlessly with other Alokai SDK modules, such as those for various eCommerce platforms like SAP Commerce Cloud.
Architecture
The Alokai SDK employs a microkernel architecture, consisting of the core Alokai SDK and various modules that extend the microkernel's functionality. A SDK module can extend the core of Alokai SDK in many different ways. In most cases, it would be a commerce module that communicates with:
- a commerce backend,
- a cms module that communicates with a CMS provider,
- a payment module that communicates with a payment provider.
However, the SDK core can be extended with virtually any type of module and not only those created by Alokai.
Installation
To extend the install a new module, for instance the SAP Commerce Cloud, you'll need to follow the below steps:
Installing the middleware API module
Remember that each time you add an SDK module, it should have a relevant middleware API Client installed as well. In Storefront, the middleware API Client should be installed into the apps/storefront-middleware
directory. Read more about each module's prerequisites in the SDK module documentation.
- Install the API Client (the server).
yarn workspace storefront-middleware add @vsf-enterprise/sapcc-api
- Install the SDK Module (the client).
yarn workspace storefront-unified-nextjs add @vsf-enterprise/sapcc-sdk
The installation process for each module varies and may necessitate additional package installations. Be sure to consult the installation guide for the specific module you intend to use.
- Update
apps/storefront-middleware/middleware.config.ts
file.
- Add new API-client integration to the config.
// apps/storefront-middleware/middleware.config.ts
const config = {
integrations: {
...,
sapcc: {
location: '@vsf-enterprise/sapcc-api/server',
configuration: {
OAuth: {
uri: process.env.SAPCC_OAUTH_URI,
clientId: process.env.SAPCC_OAUTH_CLIENT_ID,
clientSecret: process.env.SAPCC_OAUTH_CLIENT_SECRET,
tokenEndpoint: process.env.SAPCC_OAUTH_TOKEN_ENDPOINT,
tokenRevokeEndpoint: process.env.SAPCC_OAUTH_TOKEN_REVOKE_ENDPOINT,
cookieOptions: {
'vsf-sap-token': { secure: process.env.NODE_ENV !== 'development' }
}
},
api: {
uri: process.env.SAPCC_API_URI,
baseSiteId: 'apparel-uk',
catalogId: 'apparelProductCatalog',
catalogVersion: 'Online',
defaultLanguage: 'en',
defaultCurrency: 'GBP'
},
}
}
},
};
- Update
sdk.config.ts
file.
- Add the new module inside SDK configuration.
// apps/storefront-unified-nextjs/sdk/index.ts
import { sapccModule } from '@vsf-enterprise/sapcc-sdk';
export const sdk = createSdk(options, ({ buildModule, middlewareModule, middlewareUrl, getRequestHeaders }) => ({
...
sapcc: buildModule(sapccModule, {
apiUrl: `${middlewareUrl}/sapcc`,
}),
});
Now you can use both sdk.unified.<methodName>
, which returns the Unified Data Model, and sdk.sapcc.<methodName>
which returns the raw data.
Real World Examples
Adding Product Reviews with SAP SDK
In this example, we'll create a custom React hook that utilizes useQuery and sdk.sapcc.createProductReview
to add product reviews. We'll also create a form component for adding reviews and update the existing ProductReviews
component to include the form.
Creating the useAddProductReview Hook
- Create a
hooks/useAddProductReview/
directory. - Create a new file
useAddProductReview.ts
inhooks/useAddProductReview/
directory. - Import the necessary dependencies, including the
useQuery
hook and the SAP SDK module. - Define the
useAddProductReview()
function hook. - Implement the
useAddProductReview()
hook to utilize useQuery and thesdk.sapcc.createProductReview
method to add a product review. - Return the necessary variables and functions from the hook.
- Create a
index.ts
file inhooks/useAddProductReview/
directory. - Update the
index.ts
inhooks/
.
import { useMutation } from "@tanstack/react-query";
import { sdk } from "~/sdk";
export const useAddProductReview = (productCode: string) => {
return useMutation(["addProductReview", productCode], ({ review }) =>
sdk.sapcc.createProductReview({ productCode, review }),
);
};
Now, create a index.ts
file in hooks/useAddProductReview/
directory that exports a created hook:
export * from "./useAddProductReview";
Then update the index.ts
in the hooks/
directory and export useAddProductReview
module:
export * from "./useAddProductReview";
Creating the AddProductReviewForm Component
- Create
AddProductReviewForm
directory incomponents
. - Create a new file called
AddProductReviewForm.tsx
in theAddProductReviewForm
directory. - Import the necessary React components from the
@storefront-ui
library (e.g.,SfInput
,SfTextarea
). - Import the
useAddProductReview
hook. - Define the
AddProductReviewForm
component as a functional component. - Implement the form submission handler function that will utilize the
createProductReview
function from theuseAddProductReview
hook. - Render the form fields and UI components using SFUI library.
- Export the
AddProductReviewForm
component for use in other components. - Create a
index.ts
with exportedAddProductReviewForm
component. - Update the
index.ts
incomponents
.
import { useState } from 'react';
import { SfButton, SfInput, SfTextarea, SfRatingButton } from '@storefront-ui/react';
import useAddProductReview from '~/hooks';
export function AddProductReviewForm({ productId }) {
const [title, setTitle] = useState('');
const [rating, setRating] = useState(0);
const [review, setReview] = useState('');
const { mutate: createProductReview } = useAddProductReview(productId);
const handleSubmit: FormEventHandler<HTMLFormElement> = async (event) => {
event.preventDefault();
const reviewData = {
alias: title,
rating,
comment: review,
};
createProductReview(
{ review: reviewData },
{
onSuccess() {
// Handle successful review submission
setTitle('');
setRating(0);
setReview('');
},
onError() {
// Handle error
console.error('Failed to submit product review:', error);
},
},
);
};
return (
<div>
<h3 className="my-3">Write a Review</h3>
<form onSubmit={handleSubmit} className="space-y-5 mx-3">
<div>
<p id="rating">Select Rating</p>
<SfRatingButton value={rating} onChange={setRating} aria-labelledby="rating" />
</div>
<div>
<label htmlFor="title">Title:</label>
<SfInput id="title" type="text" value={title} onChange={(event) => setTitle(event.target.value)} required />
</div>
<div>
<label htmlFor="review" className="block">
Review:
</label>
<SfTextarea
id="review"
className="w-full"
value={review}
onChange={(event) => setReview(event.target.value)}
required
/>
</div>
<div>
<SfButton type="submit">Submit Review</SfButton>
</div>
</form>
</div>
);
};
export default AddProductReviewForm;
Now create the index.ts
file with the content:
export * from "./AddProductReviewForm";
And then update the index.ts
in the components
directory:
export * from "./AddProductReviewForm";
Updating the ProductReviews Component
- Import the
AddProductReviewForm
component into the existingProductReviews
component. - Add the
AddProductReviewForm
component to the JSX of theProductReviews
component.
import { SfLoaderCircular } from "@storefront-ui/react";
import { SfProductReview } from "@vsf-enterprise/unified-api-sapcc";
import { useTranslation } from "next-i18next";
import type { ProductReviewsProps } from "~/components/ProductReviews/types";
import { Review } from "~/components/ui";
import { useProductReviews } from "~/hooks";
import { AddProductReviewForm } from "../AddProductReviewForm";
// ...
export function ProductReviews({ productId }: ProductReviewsProps) {
// ...
if (!reviewsList.length) {
return (
<>
{t("noReviews")}
<AddProductReviewForm />
</>
);
}
return (
<>
{/* Existing code for displaying product reviews */}
{reviewsList.map((review) => (
<Review
className="mb-4"
key={review.id}
content={review.text ?? undefined}
author={review.reviewer ?? undefined}
title={review.title ?? undefined}
rating={review.rating ?? undefined}
date={getReviewDate(review)}
showLessText={t("readLess")}
showMoreText={t("readMore")}
/>
))}
{/* Add product review form */}
<AddProductReviewForm productId={productId} />
</>
);
}
export default ProductReviews;