The middlewareModule
The middlewareModule is the SDK module that connects your frontend to the Server Middleware. It turns your middleware endpoints into typed SDK methods — so instead of manually constructing HTTP requests, you call SDK methods and the module handles the rest.
Behind the scenes, it takes care of:
- Routing requests to the correct middleware endpoint
- Forwarding cookies and headers during SSR
- Switching between client-side and server-side API URLs
- Error handling and request logging
Setup
You can import middlewareModule directly from the @alokai/connect/sdk package:
import { initSDK, buildModule, middlewareModule } from "@alokai/connect/sdk";
import type { UnifiedEndpoints } from "storefront-middleware/types";
export const sdk = initSDK({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: "http://localhost:4000/commerce",
}),
});
If you're using @vue-storefront/next or @vue-storefront/nuxt, middlewareModule is also available as an argument in the defineSdkModule callback — alongside other helpers like buildModule, config, and getRequestHeaders:
import { defineSdkModule } from '@vue-storefront/next';
import type { CommerceEndpoints } from "storefront-middleware/types";
export const commerce = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<CommerceEndpoints>, {
apiUrl: `${config.apiUrl}/commerce`,
cdnCacheBustingId: config.cdnCacheBustingId,
defaultRequestConfig: {
headers: async () => getRequestHeaders(),
},
ssrApiUrl: `${config.ssrApiUrl}/commerce`,
}),
);
Configuration
Type definition
The middlewareModule communicates with the Alokai Middleware, which can connect to any backend. To get full type safety, you need to pass a type definition of your endpoints as a generic to middlewareModule.
In the Alokai Storefront, endpoint types are exported from storefront-middleware/types.ts. For example, UnifiedEndpoints covers all Unified API methods, and CommerceEndpoints covers raw eCommerce API methods. You can also import endpoint types directly from integration packages (e.g. @vsf-enterprise/sapcc-api).
Here's how a fully typed SDK module looks using defineSdkModule:
import { defineSdkModule } from '@vue-storefront/next';
import type { UnifiedEndpoints } from 'storefront-middleware/types';
export const unified = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: `${config.apiUrl}/commerce/unified`,
cdnCacheBustingId: config.cdnCacheBustingId,
defaultRequestConfig: {
headers: async () => getRequestHeaders(),
},
ssrApiUrl: `${config.ssrApiUrl}/commerce/unified`,
}),
);
Each module is defined in its own file under sdk/modules/ and re-exported from sdk/modules/index.ts. Then defineSdkConfig ties them together:
import { defineSdkConfig } from '@vue-storefront/next';
import * as modules from '@/sdk/modules';
export function getSdkConfig() {
return defineSdkConfig(modules);
}
The SDK instance is now fully typed — calling sdk.unified.getProduct({ id: "123" }) will autocomplete parameters and return types based on your endpoint definitions.
Options
The middlewareModule accepts the following options:
apiUrl- the URL of the middleware server.ssrApiUrl- (Optional) the URL of the middleware server during SSR.defaultRequestConfig- (Optional) default request config for each request.httpClient- (Optional) a custom HTTP client.errorHandler- (Optional) a custom error handler for HTTP requests.logger- (Optional) a flag to enable logging of the requests and responses. You can also pass a custom logger.cdnCacheBustingId- (Optional) a middleware version identifier that will be attached to allGETrequests for the sake of bypassing the CDN cache stored for a different version of the application. Read more about cache busting on MDN.
Here's an example showing all available options:
import { defineSdkModule } from '@vue-storefront/next';
import type { CommerceEndpoints } from 'storefront-middleware/types';
const GIT_COMMIT_HASH = process.env.GIT_COMMIT_HASH;
export const commerce = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<CommerceEndpoints>, {
apiUrl: `${config.apiUrl}/commerce`,
ssrApiUrl: `${config.ssrApiUrl}/commerce`,
cdnCacheBustingId: GIT_COMMIT_HASH,
defaultRequestConfig: {
headers: async () => getRequestHeaders(),
},
httpClient: async (url, params, config) => {
// Custom HTTP client
},
errorHandler: ({ error, methodName, url, params, config, httpClient }) => {
// Custom error handler
},
logger: {
onRequest: ({ url, config, params }) => {
// Custom request logger
},
onResponse: ({ url, config, response, responseTime }) => {
// Custom response logger
},
},
}),
);
Usage
Once you have added the middlewareModule to your SDK, you can use it to make requests to the Server Middleware.
const product = await sdk.commerce.getProduct({ id: "123" });
Additionally, each method of this module allows you to pass a custom request configuration. To do it, you need to import prepareConfig helper from @alokai/connect/sdk package.
import { prepareConfig } from "@alokai/connect/sdk";
const product = await sdk.commerce.getProduct(
{ id: "123" },
prepareConfig({
method: "GET",
headers: {
"X-Custom-Header": "123",
},
})
);
Examples
Send a GET request
By default, each request is a POST request. You can change it by passing a custom request configuration.
const product = await sdk.commerce.getProduct(
{ id: "123" },
prepareConfig({
method: "GET",
})
);
Use a GET method by default
You can use a GET method by default by passing it in defaultRequestConfig:
export const commerce = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<CommerceEndpoints>, {
apiUrl: `${config.apiUrl}/commerce`,
defaultRequestConfig: {
method: "GET",
headers: async () => getRequestHeaders(),
},
ssrApiUrl: `${config.ssrApiUrl}/commerce`,
}),
);
Add a header to each request
Pass static headers in defaultRequestConfig:
export const commerce = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<CommerceEndpoints>, {
apiUrl: `${config.apiUrl}/commerce`,
defaultRequestConfig: {
headers: {
"X-Api-Key": "123",
},
},
ssrApiUrl: `${config.ssrApiUrl}/commerce`,
}),
);
The headers field also accepts an async function, which is useful when you need to combine request headers with custom values or fetch a token dynamically before each request:
export const commerce = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<CommerceEndpoints>, {
apiUrl: `${config.apiUrl}/commerce`,
defaultRequestConfig: {
headers: async () => ({
...(await getRequestHeaders()),
Authorization: `Bearer ${await getAccessToken()}`,
}),
},
ssrApiUrl: `${config.ssrApiUrl}/commerce`,
}),
);
Add a header to a single request
Pass headers in the prepareConfig call for a single request:
const product = await sdk.commerce.getProduct(
{ id: "123" },
prepareConfig({
headers: {
"X-Custom-Header": "123",
},
})
);
Log requests and responses
Set the logger option to true to enable request and response logging:
export const commerce = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<CommerceEndpoints>, {
apiUrl: `${config.apiUrl}/commerce`,
defaultRequestConfig: {
headers: async () => getRequestHeaders(),
},
logger: true,
ssrApiUrl: `${config.ssrApiUrl}/commerce`,
}),
);
You can also enable logging across all middleware modules by setting the ALOKAI_SDK_DEBUG environment variable to true. If logger is explicitly set to false, the environment variable is ignored.
To override the default logger, pass a custom logger object:
export const commerce = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<CommerceEndpoints>, {
apiUrl: `${config.apiUrl}/commerce`,
defaultRequestConfig: {
headers: async () => getRequestHeaders(),
},
logger: {
onRequest: ({ url, config, params }) => {
console.log(`Request: ${config.method} ${url}`, params);
},
onResponse: ({ url, config, responseTime }) => {
console.log(`Response: ${config.method} ${url} in ${responseTime.toFixed()}ms`);
},
},
ssrApiUrl: `${config.ssrApiUrl}/commerce`,
}),
);
Replace the default HTTP client with axios
By default, the SDK uses the fetch API. You can replace it via the httpClient option.
A custom HTTP client must:
- Be an async function accepting
url,params, andconfigparameters. - Send credentials with the request (e.g.
withCredentials: truein axios) so cookies are forwarded. - Throw an
SdkHttpErrorif the request fails.
import axios from "axios";
import { SdkHttpError } from "@alokai/connect/sdk";
export const commerce = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<CommerceEndpoints>, {
apiUrl: `${config.apiUrl}/commerce`,
defaultRequestConfig: {
headers: async () => getRequestHeaders(),
},
httpClient: async (url, params, config) => {
try {
const { data } = await axios(url, {
...config,
data: params,
withCredentials: true,
});
return data;
} catch (error: any) {
throw new SdkHttpError({
statusCode: error.response?.status || 500,
message: error.response?.data?.message || error.message,
cause: error,
});
}
},
ssrApiUrl: `${config.ssrApiUrl}/commerce`,
}),
);
Without withCredentials: true (axios) or credentials: 'include' (fetch), cookies won't be sent with cross-origin requests.
Add a custom error handler
Use the errorHandler option to intercept and handle errors. For example, to automatically refresh an expired token and retry:
import { refreshToken } from "@/handlers/refreshToken";
export const commerce = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<CommerceEndpoints>, {
apiUrl: `${config.apiUrl}/commerce`,
defaultRequestConfig: {
headers: async () => getRequestHeaders(),
},
errorHandler: async ({ error, methodName, url, params, config, httpClient }) => {
if (error.status === 401 && methodName !== "login") {
await refreshToken();
return httpClient(url, params, config);
}
throw error;
},
ssrApiUrl: `${config.ssrApiUrl}/commerce`,
}),
);
Add cookies to the request during SSR
During CSR, cookies are sent automatically. During SSR, the getRequestHeaders helper provided by defineSdkModule takes care of forwarding them — as shown in the Setup and Configuration sections.
In Next.js, the getSdk function from @/sdk calls Next.js's cookies() and headers() internally, so you just await it in any server component:
import { getSdk } from "@/sdk";
export default async function SsrPage() {
const sdk = await getSdk();
const product = await sdk.commerce.getProduct({ id: "123" });
return (
// ...
);
}
In Nuxt, cookies are forwarded automatically — no extra setup needed.
Add cookies to the request during CSR
Cookies are sent with every request during Client-Side Rendering automatically — no extra configuration is needed. The SDK uses credentials: "include" under the hood, so the browser attaches all relevant cookies to each request.
If you need to include a custom cookie, set it via document.cookie before making the SDK call — the browser will include it in all subsequent requests to the matching domain:
document.cookie = "name=value; path=/";
const product = await sdk.commerce.getProduct({ id: "123" });
Use the middlewareModule with a custom integration
You can use middlewareModule with any custom integration by providing a type definition of its endpoints.
This example assumes you have a custom API Client implemented as described in the Creating an API Client guide, and that the integration is registered in middleware.config.ts at the /custom endpoint.
First, re-export the Endpoints type from the custom integration package so your frontend doesn't need a direct dependency on it:
export { Endpoints as CustomEndpoints } from "custom-integration-api-client";
// ...
Then, create a new SDK module for the custom integration:
import { defineSdkModule } from "@vue-storefront/next";
import type { CustomEndpoints } from "storefront-middleware/types";
export const custom = defineSdkModule(({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<CustomEndpoints>, {
apiUrl: `${config.apiUrl}/custom`,
defaultRequestConfig: {
headers: async () => getRequestHeaders(),
},
ssrApiUrl: `${config.ssrApiUrl}/custom`,
}),
);
Now you can call the custom integration endpoints through the SDK:
const result = await sdk.custom.someMethod({ id: "123" });