The middlewareModule
Introduction
The middlewareModule
is an SDK module that ensures communication with the Server Middleware.
Setup
Depending on which framework you are using, you can get the middlewareModule
in different ways. However, the setup is always the same.
In Next, middlewareModule
is available in the createSdk
function.
// sdk/sdk.config.ts
import { CreateSdkOptions, createSdk } from "@vue-storefront/next";
import { UnifiedEndpoints } from "storefront-middleware/types";
const options: CreateSdkOptions = {
middleware: {
apiUrl: "http://localhost:4000",
},
};
export const { getSdk } = createSdk(
options,
({ buildModule, config, middlewareModule, getRequestHeaders }) => ({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: config.middlewareUrl + "/commerce",
defaultRequestConfig: {
headers: getRequestHeaders(),
},
}),
})
);
In Nuxt, middlewareModule
is available in the defineSdkConfig
function.
// sdk.config.ts
import { UnifiedEndpoints } from "storefront-middleware/types";
export default defineSdkConfig(
({ buildModule, config, middlewareModule, getRequestHeaders }) => ({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: config.middlewareUrl + "/commerce", // SAP Commerce Cloud integration is available at /commerce endpoint
defaultRequestConfig: {
headers: getRequestHeaders(),
},
})
})
);
You can also import it directly from the @vue-storefront/sdk
package.
import { initSDK, buildModule, middlewareModule } from "@vue-storefront/sdk";
import { UnifiedEndpoints } from "storefront-middleware/types";
export const sdk = initSDK({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: "http://localhost:8181/commerce",
}),
});
Configuration
Type definition
As the middlewareModule
is used to communicate with the Alokai Middleware and it's not limited to a specific backend, it requires a type definition of the endpoints.
The type definition should be provided as a generic type to the middlewareModule
function.
type Endpoints = {
getProduct: ({
id: string,
}) => Promise<{ id: string; title: string; description: string }>;
};
const sdk = initSDK({
commerce: buildModule(middlewareModule<Endpoints>, {
apiUrl: "http://localhost:8181/commerce",
}),
});
const product = await sdk.commerce.getProduct({ id: "123" });
In Alokai Storefront, you can find the UnifiedEndpoints
type in storefront-middleware/types.ts
file. It's a type definition for the endpoints used in the middleware.
Nuxt and Next examples
We're using initSDK
approach in this examples to make it more universal. However, you can use createSdk
and defineSdkConfig
functions in Next and Nuxt as well.
import { UnifiedEndpoints } from "storefront-middleware/types";
export const sdk = initSDK({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: "http://localhost:8181/commerce",
}),
});
const product = await sdk.commerce.getProduct({ id: "123" });
Also, you can import the Endpoints
type from integration packages.
import { Endpoints } from "@vsf-enterprise/sapcc-api";
export const sdk = initSDK({
commerce: buildModule(middlewareModule<Endpoints>, {
apiUrl: "http://localhost:8181/commerce",
}),
});
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.
Example configuration:
import { initSDK, buildModule, middlewareModule } from "@vue-storefront/sdk";
import { UnifiedEndpoints } from "storefront-middleware/types";
export const sdk = initSDK({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: "/api/commerce",
ssrApiUrl: "http://localhost:8181/commerce",
defaultRequestConfig: {
headers: {
"X-Api-Key": "123",
},
},
httpClient: async (url, params, config) => {
// Custom HTTP client
},
errorHandler: ({ error, methodName, url, params, config, httpClient }) => {
// Custom error handler
},
logger: {
request: (url, config) => {
// custom request logger
},
response: (response) => {
// custom response logger
},
}
}),
});
Once you have added the middlewareModule
to your SDK, you can use it to make requests to the Alokai Middleware.
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 @vue-storefront/sdk
package.
import { prepareConfig } from "@vue-storefront/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 the defaultRequestConfig
option of the middlewareModule
function.
export const sdk = initSDK({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: "http://localhost:8181/commerce",
defaultRequestConfig: {
method: "GET",
},
}),
});
Add a header to each request
You can add a header to each request by passing it in the defaultRequestConfig
option of the middlewareModule
function.
export const sdk = initSDK({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: "http://localhost:8181/commerce",
defaultRequestConfig: {
headers: {
"X-Api-Key": "123",
},
},
}),
});
Add a header to single request
You can add a header to a single request by passing it in the request configuration.
const product = await sdk.commerce.getProduct(
{ id: "123" },
prepareConfig({
headers: {
"X-Custom-Header": "123",
},
})
);
Log requests and responses
You can enable logging of the requests and responses by setting the logger
option to true
in the middlewareModule
function.
export const sdk = initSDK({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: "http://localhost:8181/commerce",
logger: true,
}),
});
You can also enable logging of the requests and responses in all middleware modules by setting the environment variable ALOKAI_SDK_DEBUG
to true
. If logger
is set to false
, the environment variable will be ignored.
If you want to override the default logger, you can pass a custom logger to the logger
option.
export const sdk = initSDK({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: "http://localhost:8181/commerce",
logger: {
request: (url, config) => {
console.log(`Request: ${url}`);
},
response: (response) => {
console.log(`Response: ${response}`);
},
},
}),
});
Replace the default HTTP client with axios
By default, the SDK uses the fetch
API.
You can replace the default HTTP client with axios
by passing it in the httpClient
option of the middlewareModule
function.
There are some requirements for the custom HTTP client.
- It must be an async function accepting the following parameters:
url
- the URL of the request,params
- the parameters of the request,config
- the request configuration,
- It must include the
Access-Control-Allow-Credentials
response header (more info here), - It must throw an
SdkHttpError
if the request fails.
import axios from "axios";
import { SdkHttpError } from "@vue-storefront/sdk";
export const sdk = initSDK({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: "http://localhost:8181/commerce",
httpClient: async (url, params, config) => {
try {
const { data } = await axios(url, {
...config,
data: params,
withCredentials: true, // Includes the `Access-Control-Allow-Credentials` response header
});
return data;
} catch (error: any) {
throw new SdkHttpError({
statusCode: error.response?.status || 500,
message: error.response?.data?.message || error.message,
cause: error,
});
}
},
}),
});
It's important to include the Access-Control-Allow-Credentials
response header in the custom HTTP client. Otherwise, the cookies won't be sent with the request.
Add a custom error handler
You can add a custom error handler by passing it in the errorHandler
option of the middlewareModule
function.
import { SdkHttpError } from "@vue-storefront/sdk";
import { refreshToken } from "./handlers/refreshToken"; // Custom implementation of the refresh token logic
const options: Options = {
apiUrl: "https://api.example.com",
errorHandler: async ({
error,
methodName,
url,
params,
config,
httpClient,
}) => {
if (error.status === 401 && methodName !== "login") {
// Refresh the token
await refreshToken();
// Retry the request
return httpClient(url, params, config);
}
throw error;
},
};
Add cookies to the request during SSR
Normally, the cookies are sent with the request during CSR. However, during SSR, you need to pass them manually.
@vue-storefront/next
and @vue-storefront/nuxt
provide a getRequestHeaders
helper that returns the cookies.
To use it, you need to pass the getRequestHeaders
function to the defaultRequestConfig
option of the middlewareModule
function.
import { defineSdkConfig } from "@vue-storefront/next";
import { UnifiedEndpoints } from "storefront-middleware/types";
export function getSdkConfig() {
return defineSdkConfig(({ buildModule, config, middlewareModule, getRequestHeaders }) => ({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: config.middlewareUrl + "/commerce",
defaultRequestConfig: {
headers: getRequestHeaders(),
},
}),
}));
}
// sdk.config.ts
import { defineSdkConfig } from "@vue-storefront/nuxt";
import { UnifiedEndpoints } from "storefront-middleware/types";
export default defineSdkConfig(
({ buildModule, config, middlewareModule, getRequestHeaders }) => ({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: config.middlewareUrl + "/commerce",
defaultRequestConfig: {
headers: getRequestHeaders(),
},
}),
})
);
While Nuxt ensures that cookies are sent with the request without any additional configuration, in Next, you need to pass the headers to getSdk
function.
import { headers } from "next/headers";
import { getSdk } from "@/sdk";
export default async function SsrPage() {
const sdk = getSdk({
getRequestHeaders: () => headers(),
});
const product = await sdk.commerce.getProduct({ id: "123" });
return (
// ...
);
}
import { GetServerSideProps } from "next";
import { getSdk } from "@/sdk";
export default function SsrPage({ result }: any) {
return (
// ...
);
}
export const getServerSideProps: GetServerSideProps = async ({ req }) => {
const sdk = getSdk({
getRequestHeaders: () => req.headers,
});
return {
props: {
result: await sdk.commerce.getProduct({ id: "123" }),
},
};
};
Add cookies to the request during CSR
Cookies are being sent with the request during Client-Side Rendering by default.
However, you can always specify the headers manually by passing them to the defaultRequestConfig
option of the middlewareModule
...
export const sdk = initSDK({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: "http://localhost:8181/commerce",
defaultRequestConfig: {
headers: {
Cookie: "name=value",
},
},
}),
});
...or by passing them to the request configuration.
import { prepareConfig } from "@vue-storefront/sdk";
const product = await sdk.commerce.getProduct(
{ id: "123" },
prepareConfig({
headers: {
Cookie: "name=value",
},
})
);
Use the middlewareModule
with a custom integration
You can use the middlewareModule
with a custom integration by providing a type definition of the endpoints.
This example assumes that you have a custom Api Client that has been implemented as described in the Creating an API Client guide. It also assumes that the integration has been added to the middleware.config.ts
and it's available at the /custom
endpoint.
First, let's export the Endpoints
type as CustomEndpoints
from the custom integration package.
That way, you won't need to install the custom integration package on the frontend and you'll avoid naming conflicts.
export { Endpoints as CustomEndpoints } from "custom-integration-api-client";
// ...
Then, let's initialize the SDK with the middlewareModule
and the CustomEndpoints
type.
import { initSDK, buildModule, middlewareModule } from "@vue-storefront/sdk";
import { CustomEndpoints } from "storefront-middleware/types";
export const sdk = initSDK({
custom: buildModule(middlewareModule<CustomEndpoints>, {
apiUrl: "http://localhost:8181/custom",
}),
});
Now, you can use the custom
module to make requests to the custom integration endpoints.
const result = await sdk.custom.someMethod({ id: "123" });