diff --git a/docs/content/3.middleware/2.guides/4.orchestration.md b/docs/content/3.middleware/2.guides/4.orchestration.md index 9493d5ddf1..3a15f4c0be 100644 --- a/docs/content/3.middleware/2.guides/4.orchestration.md +++ b/docs/content/3.middleware/2.guides/4.orchestration.md @@ -7,6 +7,7 @@ Optimizing server requests through data integration and orchestration is essenti Data orchestration allows for consolidating multiple server requests into a single endpoint, which significantly eases the burden on the frontend. This is particularly beneficial in scenarios involving numerous simultaneous server requests. ### Advantages: + - **Minimized Network Traffic**: Fewer server calls lead to reduced latency and enhanced responsiveness. - **Simplified Frontend Architecture**: By grouping related server requests, the frontend logic becomes less complex. - **Uniform Data Collection**: Ensures that data fetched from different sources is consistent and provided in a standard format. @@ -27,7 +28,7 @@ The `getApiClient` method takes a single argument, which is the key of the api c Here's a basic example of what this might look like: -```javascript +```typescript [middleware.config.ts] export const integrations = { sapcc: { location: "@vsf-enterprise/sapcc-api/server", @@ -40,11 +41,11 @@ export const integrations = { name: "sapcc-contentful-extension", extendApiMethods: { getPDP: async (context, params: { id: string }) => { - const sapcc = context.getApiClient("sapcc"); - const contentful = context.getApiClient("contentful"); + const sapccApi = context.api; // You can access integration methods directly + const contentful = context.getApiClient("contentful"); // You can access other integrations using getApiClient const [product, content] = Promise.all( - sapcc.api.getProduct({ id: params.id }), + sapccApi.getProduct({ id: params.id }), contentful.api.getEntries({ content_type: "product", "fields.sku": params.id, @@ -75,63 +76,156 @@ export const integrations = { 3. Aggregate Data: Once data from all required integrations is retrieved, aggregate and format it as needed. -4. Return Unified Response: Send a consolidated response back to the frontend.: +4. Return Unified Response: Send a consolidated response back to the frontend. ### Using orchestration methods in the frontend -To call the orchestration endpoint, you need to send a POST request to the endpoint URL. In our example it would be `POST http://localhost:8080/sapcc/getPDP`. The request body should contain the parameters required by the endpoint. - -When it comes to extending the Alokai integrations like `sapcc` or `contentful`, you can use the SDK extension mechanism as described in the [Extending a Module](/sdk/advanced/extending-module) guide. - -## Real-World Examples +To call the orchestration endpoint, you can use the [middlewareModule](/sdk/getting-strated/middlewareModule). -The examples provided demonstrate practical uses of data orchestration: +You need to prepare the type definition for the orchestration methods. It would be necessary to pass this type to the `middlewareModule`. To do it, let's aggregate the orchestration methods in a single object: -### Example 1: Fetching Custom Product Properties from Legacy Systems +```typescript [storefront-middleware/middleware.config.ts] +export const orchestrationMethods = { + getPDP: async (context, params: { id: string }) => { + const sapccApi = context.api; + const contentful = context.getApiClient("contentful"); -This use case involves calling the commerce backend to fetch specific product data. Additionally, a separate call is made to a legacy custom system of the customer, to retrieve a custom product property (e.g., stock of the product). This data is used, for example, to display stock information on the product page. + const [product, content] = Promise.all( + sapccApi.getProduct({ id: params.id }), + contentful.api.getEntries({ + content_type: "product", + "fields.sku": params.id, + }) + ); -Example implementation might look like this: + return { + product, + content, + }; + }, +}; -```typescript [middleware.config.ts] export const integrations = { sapcc: { - // ... + location: "@vsf-enterprise/sapcc-api/server", + configuration: { + // ... + }, extensions: (extensions) => [ ...extensions, { - name: "orchestration-extension", + name: "sapcc-contentful-extension", extendApiMethods: { - enrichedSearch: async (context, params: { productId: string }) => { - const sapcc = context.getApiClient("sapcc"); - const legacyCustomSystem = context.getApiClient("legacyCustomSystem"); - - const prouctStock = await legacyCustomSystem.api.getProductStock({ - productId: params.productId, - }); - - const product = await sapcc.api.getProduct({ - { id: params.productId }, - }); - - return { - ...product, - stock: productStock, - }; - }, + ...orchestrationMethods, }, }, ], }, - legacyCustomSystem: { - // ... + contentful: { + location: "@vsf-enterprise/contentful-api/server", + configuration: { + // ... + }, + }, +}; +``` + +Then, let's use `WithoutContext` type helper to prepare the type of the endpoints created by the orchestration methods. As a good practice, it's recommended to use a separate file for the types used in the middleware configuration: + +```typescript [storefront-middleware/types.ts] +import { type WithoutContext } from "@vue-storefront/middleware"; +import { orchestrationMethods } from "./middleware.config"; + +export { type Endpoints as SapccEndpoints} from "@vsf-enterprise/sapcc-api"; +export type OrchestrationEndpoints = WithoutContext; +``` + + +Finally, pass the `OrchestrationEndpoints` type to the `middlewareModule`: + +::code-group +```typescript[Next.js] +import { contentfulModule } from "@vsf-enterprise/contentful-sdk"; +import { CreateSdkOptions, createSdk } from "@vue-storefront/next"; +import { SapccEndpoints, OrchestrationEndpoints } from "storefront-middleware/types"; + +const options: CreateSdkOptions = { + middleware: { + apiUrl: "http://localhost:4000", }, }; + +export const { getSdk } = createSdk( + options, + ({ buildModule, middlewareUrl, middlewareModule, getRequestHeaders }) => ({ + sapcc: buildModule(middlewareModule, { + apiUrl: middlewareUrl + "/sapcc", + defaultRequestConfig: { + headers: getRequestHeaders(), + }, + }), + contentful: buildModule(contentfulModule, { + apiUrl: middlewareUrl + "/contentful", + }), + }) +); +``` + +```typescript[Nuxt.js] +import { contentfulModule } from "@vsf-enterprise/contentful-sdk"; +import { SapccEndpoints, OrchestrationEndpoints } from "storefront-middleware/types"; + +export default defineSdkConfig( + ({ buildModule, middlewareUrl, middlewareModule getRequestHeaders }) => ({ + sapcc: buildModule(middlewareModule, { + apiUrl: middlewareUrl + "/sapcc", + defaultRequestConfig: { + headers: getRequestHeaders(), + }, + }), + contentful: buildModule(contentfulModule, { + apiUrl: middlewareUrl + "/contentful", + }), + }) +); +``` + +```typescript[Other] +import { initSDK, buildModule, middlewareModule } from "@vue-storefront/sdk"; +import { contentfulModule } from "@vsf-enterprise/contentful-sdk"; +import { SapccEndpoints, OrchestrationEndpoints } from "storefront-middleware/types"; + +const { sapcc } = initSDK({ + sapcc: buildModule(middlewareModule, { + apiUrl: "http://localhost:8181/sapcc", + }), +}); + +const { contentful } = initSDK({ + contentful: buildModule(contentfulModule, { + apiUrl: "http://localhost:8181/contentful", + }), +}); + +export { sapcc, contentful }; +``` +:: + +Now, the SDK is aware of the orchestration methods, and you can call them as follows: + +```typescript +const pdp = await sdk.sapcc.getPDP({ id: "123" }); ``` -### Example 2: Product Slider Orchestration with Commerce Backend Data +## Real-World Examples + +The examples provided demonstrate practical uses of data orchestration: + +### Example 1: Fetching Custom Product Properties from Legacy Systems + +This use case involves calling the commerce backend to fetch specific product data. Additionally, a separate call is made to a legacy custom system of the customer, to retrieve a custom product property (e.g., stock of the product). This data is used, for example, to display stock information on the product page. -In this scenario, the CMS returns a product slider component that lacks sufficient product data (or contains only product IDs). The orchestration layer enhances the product slider by adding product data from the commerce backend, ensuring a comprehensive and informative display. +Example implementation might look like this: ```typescript [middleware.config.ts] export const integrations = { @@ -142,45 +236,29 @@ export const integrations = { { name: "orchestration-extension", extendApiMethods: { - getProductSlider: async ( - context: IntegrationContext, - props: { entryId: string } - ) => { - const contentful: ApiClient = - context.getApiClient("contentful"); - const sapcc: ApiClient = - context.getApiClient("sapcc"); - - const content = await contentful.api.getEntry(props.entryId); - const productItems = await Promise.all( - content.fields.items.map(async (item) => { - const product = await sapcc.api.getProduct({ - id: item.fields.productId, - }); - - return { - ...item, - fields: { - ...item.fields, - ...product, - }, - }; - }) - ); + enrichedSearch: async (context, params: { productId: string }) => { + const sapccApi = context.api; + const legacyCustomSystem = context.getApiClient("legacyCustomSystem"); + + const [prouctStock, product] = await Promise.all([ + legacyCustomSystem.api.getProductStock({ + productId: params.productId, + }), + sapccApi.getProduct({ + { id: params.productId }, + }), + ]); return { - ...content, - fields: { - ...content.fields, - items: productItems, - }, + ...product, + stock: productStock, }; }, }, }, ], }, - contentful: { + legacyCustomSystem: { // ... }, }; @@ -204,37 +282,13 @@ import { } from "@vsf-enterprise/sapcc-api"; ``` -### Endpoints and the `context` object - -Each method of the integration api client contains the `context` object as the first argument. However, the `context` is not something that is passed by the developer to the method during the call. This is because the `context` is passed automatically by the `@vue-storefront/middleware` package logic. Because of this, the `context` object should be excluded from the `Api` interface passed to the `ApiClient` type. - -To achieve that, the `@vue-storefront/middleware` export the interface `ContextualizedApi`, which basically removes the context from the API. - -### Example - -Let's take a look at the `sapcc` integration. The `sapcc` integration exports the following types: - -```typescript -import { - Endpoints, - MiddlewareConfig, - AxiosInstance, -} from "@vsf-enterprise/sapcc-api"; -import { ContextualizedApi } from "@vue-storefront/middleware"; - -// ... +:::tip Type of endpoints -const sapcc = context.getApiClient< - ContextualizedApi, - MiddlewareConfig, - AxiosInstance ->("sapcc"); +Sometimes, the `Endpoints` type is not exported by the integration. If that's the case, you can import the `XyzIntegrationContext` type from the integration package. For example, the `sapcc` integration exports the `SapccIntegrationContext` type, which contains the following: -// sapcc.api now is aware of the SAPCC methods that are not expecting the `context` object -// sapcc.config is now aware of the SAPCC configuration object -// sapcc.client is now aware of the SAPCC HTTP client object -``` +- `SapccIntegrationContext['api']` - the endpoints type +- `SapccIntegrationContext['config']` - the configuration object type +- `SapccIntegrationContext['client']` - the HTTP client object type -:::tip Type of endpoints -Sometimes, the `Endpoints` type is not exported by the integration. If that's the case, you can import the `XyzIntegrationContext` type from the integration package. For example, the `sapcc` integration exports the `SapccIntegrationContext` type, which contains the endpoints type as `SapccIntegrationContext['api']`. It contains also the type of the configuration object as `SapccIntegrationContext['config']` and the type of the HTTP client object as `SapccIntegrationContext['client']`. This should be applied to all integrations. +This applies to all integrations. ::: diff --git a/docs/content/5.integrations/3.custom/3.api-client.md b/docs/content/3.middleware/2.guides/7.api-client.md similarity index 56% rename from docs/content/5.integrations/3.custom/3.api-client.md rename to docs/content/3.middleware/2.guides/7.api-client.md index c55996a69d..a473ae2853 100644 --- a/docs/content/5.integrations/3.custom/3.api-client.md +++ b/docs/content/3.middleware/2.guides/7.api-client.md @@ -1,7 +1,5 @@ # Creating an API Client - - The API client is used by the server middleware to create a server-to-server communication with your custom backend. ## Creating the integration client @@ -12,7 +10,7 @@ First, you should create the `index.server.ts` file. It will be the entry point ```ts // index.server.ts -import { apiClientFactory } from '@vue-storefront/middleware'; +import { apiClientFactory } from "@vue-storefront/middleware"; const onCreate = (settings: any) => { // TODO: create a client here and return it with the integration configuration @@ -32,15 +30,16 @@ The `onCreate` function is called when the server middleware is initialized. It The `api` object is a set of functions that will be available in the integration client. -Now, let's create the client. +Now, let's create the client. :::tip -In the following example we used the `axios` library to create a client. However, you can use any client that you suits your needs. +In the following example we used the `axios` library to create a client. However, you can use any client that suits your needs. + ::: The `buildClient` function creates an instance of the `axios` client. It's a good practice to create a separate function for creating the client, so you can easily mock it in the tests. -The `onCreate` function returns accepts the integration configuration, and we recommend that you create an interface for it. +The `onCreate` function accepts the integration configuration, and we recommend that you create an interface for it. ```ts // types/config/index.ts @@ -57,20 +56,20 @@ You should use the `MiddlewareConfig` interface in the `onCreate` function. ```ts // index.server.ts -import { apiClientFactory } from '@vue-storefront/middleware'; -import axios from 'axios'; -import { MiddlewareConfig } from './types/config'; +import { apiClientFactory } from "@vue-storefront/middleware"; +import axios from "axios"; +import { MiddlewareConfig } from "./types/config"; const buildClient = () => { const axiosInstance = axios.create(); return axiosInstance; }; -const onCreate = (settings: MiddlewareConfig) => { +const onCreate = (config: MiddlewareConfig) => { const client = buildClient(); return { - config: settings, + config, client, }; }; @@ -83,20 +82,15 @@ const { createApiClient } = apiClientFactory({ export { createApiClient }; ``` -The server middleware can be initialized now, but it does not contain any API functions. Before adding them, let's type the integration context. Alokai uses the `context` object to pass the integration client and the configuration to the API functions. Create a `types/context/index.ts` file and add the following code: +Now, we can initialize the server middleware, but it does not contain any API methods. Before adding them, let's create a type for the integration context. ```ts // types/context/index.ts -import { IntegrationContext } from '@vue-storefront/middleware'; -import { AxiosInstance } from 'axios'; -import { MiddlewareConfig } from '../config'; +import { IntegrationContext } from "@vue-storefront/middleware"; +import { AxiosInstance } from "axios"; +import { MiddlewareConfig } from "../config"; -/** - * All available API Endpoints without first argument - `context`, because this prop is set automatically. - */ -export type ContextualizedEndpoints = { - [T in keyof Endpoints]: Endpoints[T] extends (x: any, ...args: infer P) => infer R ? (...args: P) => R : never; -}; +export type TODO = any; /** * Runtime integration context, which includes API client instance, settings, and endpoints that will be passed via middleware server. @@ -105,71 +99,91 @@ export type ContextualizedEndpoints = { export type MyIntegrationIntegrationContext = IntegrationContext< AxiosInstance, // HTTP client instance MiddlewareConfig, - ContextualizedEndpoints + TODO >; +``` -/** - * Global context of the application which includes runtime integration context. - **/ -export interface Context { - // This property is named `myIntegration`, but you should use your integration name in here. - $myIntegration: MyIntegrationIntegrationContext; -} +Now, you can create the first API method - an `exampleEndpoint` function. + +```ts +// api/exampleEndpoint/index.ts +import { MyIntegrationIntegrationContext, TODO } from "../../types"; + +export const exampleEndpoint = async ( + context: MyIntegrationIntegrationContext, + params: TODO +) => { + console.log("exampleEndpoint has been called"); + + // Example request could look like this: + // return await context.client.get(`example-url?id=${params.id}`); + return Promise.resolve({ success: true }); +}; ``` -The `ContextualizedEndpoints` type is a set of API functions without the `context` argument. It's necessary for the `IntegrationContext` type. It also requires the type of client used in the integration and the type of integration configuration. +You should also export the `exampleEndpoint` function in the `api/index.ts` file. -The `Context` type is the global context of the application. It's used by api-client methods to access the integration client and the configuration. +```ts +// api/index.ts +export * from "./exampleEndpoint"; +``` -Now you can add the interface for the API functions. +Then, let's create the `Endpoints` type. ```ts -// types/api/index.ts -import { MyIntegrationContext } from '../config'; +// types/api/endpoints.ts +import { WithoutContext } from "@vue-storefront/middleware"; +import * as apiMethods from "../../api"; -type TODO = any; +export type ApiMethods = typeof apiMethods; -/** - * Definition of all API-client methods available in {@link https://docs.alokai.com/v2/advanced/context.html#context-api | context}. - */ -export interface Endpoints { - /** - * Here you can find an example endpoint definition. Based on this example, you should define how your endpoint will look like. - * This description will appear in the API extractor, so try to document all endpoints added here. - */ - exampleEndpoint: (context: MyIntegrationContext, params: TODO) => Promise; -} +export type Endpoints = WithoutContext; ``` -Finally, you can create the `exampleEndpoint` function. +Notice that we use the `WithoutContext` type from the `@vue-storefront/middleware` package. It's a utility type that removes the `context` parameter from the API methods. +Server Middleware creates the `context` object based on the middleware configuration and request that has been made. The `Endpoints` interface should reflect only the available API endpoints. + +Finally, let's add the `Endpoints` type to the `MyIntegrationIntegrationContext` interface. ```ts -// api/exampleEndpoint/index.ts -import { Endpoints } from '../../types'; +// types/context/index.ts +import { IntegrationContext } from "@vue-storefront/middleware"; +import { AxiosInstance } from "axios"; +import { MiddlewareConfig } from "../config"; +import { Endpoints } from "../api/endpoints"; -export const exampleEndpoint: Endpoints['exampleEndpoint'] = async (context, params) => { - console.log('exampleEndpoint has been called'); +export type TODO = any; - // Example request could look like this: - // return await context.client.get(`example-url?id=${params.id}`); - return Promise.resolve({ success: true }); -}; +export type MyIntegrationIntegrationContext = IntegrationContext< + AxiosInstance, // HTTP client instance, you should use your client type here + MiddlewareConfig, + Endpoints +>; ``` -You should also export the `exampleEndpoint` function in the `api/index.ts` file. +Remember to export all the types in the `types/index.ts` file. ```ts -// api/index.ts -export * from './exampleEndpoint'; +// types/index.ts +export * from "./config"; +export * from "./context"; +export * from "./api/endpoints"; +``` + +And from `index.ts` as well. + +```ts +// index.ts +export * from "./types"; ``` To be able to call the `exampleEndpoint` function, you should add it to the `api` object in the `index.server.ts` file. ```ts -import { apiClientFactory } from '@vue-storefront/middleware'; -import axios from 'axios'; -import * as api from './api'; -import { MiddlewareConfig } from './types/config'; +import { apiClientFactory } from "@vue-storefront/middleware"; +import axios from "axios"; +import * as api from "./api"; +import { MiddlewareConfig } from "./types/config"; const buildClient = () => { const axiosInstance = axios.create(); @@ -207,7 +221,7 @@ This guide described the details of creating the integration, but it does not co module.exports = { integrations: { boilerplate: { - location: '@vsf-enterprise/my-integration-api/server', // This should be the path to your built index.server.js file + location: "@vsf-enterprise/my-integration-api/server", // This should be the path to your built index.server.js file configuration: { // Add your configuration here }, @@ -220,20 +234,22 @@ You can run the server middleware with this example script: ```js // server.js -const { createServer } = require('@vue-storefront/middleware'); -const { integrations } = require('./middleware.config'); -const cors = require('cors'); +const { createServer } = require("@vue-storefront/middleware"); +const { integrations } = require("./middleware.config"); +const cors = require("cors"); (async () => { const app = await createServer({ integrations }); - const host = process.argv[2] ?? '0.0.0.0'; + const host = process.argv[2] ?? "0.0.0.0"; const port = process.argv[3] ?? 8181; - const CORS_MIDDLEWARE_NAME = 'corsMiddleware'; + const CORS_MIDDLEWARE_NAME = "corsMiddleware"; - const corsMiddleware = app._router.stack.find((middleware) => middleware.name === CORS_MIDDLEWARE_NAME); + const corsMiddleware = app._router.stack.find( + (middleware) => middleware.name === CORS_MIDDLEWARE_NAME + ); corsMiddleware.handle = cors({ - origin: ['http://localhost:3000'], + origin: ["http://localhost:3000"], credentials: true, }); diff --git a/docs/content/4.sdk/1.index.md b/docs/content/4.sdk/1.index.md index 4bfe719aad..6770816d94 100644 --- a/docs/content/4.sdk/1.index.md +++ b/docs/content/4.sdk/1.index.md @@ -27,9 +27,9 @@ By utilizing the Alokai SDK, you can establish a type-safe contract between your The SDK has two components: 1. The SDK Core - the core package, `@vue-storefront/sdk`, that initializes all modules and implements the plug-in architecture -2. Modules - pluggable pieces of code as standalone packages that integrate into the core to add functionality (eg.: `@vsf-enterprise/sapcc-sdk`) +2. Modules - pluggable pieces of code as standalone packages that integrate into the core to add functionality (eg.: `middlewareModule`) -In most cases, these modules will come through our [Integrations](/integrations) - which contain both an SDK Module and an API Client (Middleware) - but you can also create your own modules. +In most cases, you would use `middlewareModule` with different configurations - but you can also create your own modules. :card{to="/integrations" title="Integrations" description="See all of the available integrations that you can use with the SDK" icon="fluent:puzzle-cube-piece-20-filled"} diff --git a/docs/content/4.sdk/2.getting-started/1.index.md b/docs/content/4.sdk/2.getting-started/1.index.md index 4d6c3d436f..5c0a9d4e8f 100644 --- a/docs/content/4.sdk/2.getting-started/1.index.md +++ b/docs/content/4.sdk/2.getting-started/1.index.md @@ -1,8 +1,12 @@ -# Getting Started +# Getting Started -If you're setting your Alokai application from scratch, you'll need to configure the SDK Core to create a type-safe SDK that communicates with your [Server Middleware](/middleware). +If you're setting your Alokai application from scratch, you'll need to configure a type-safe SDK that communicates with your [Server Middleware](/middleware). -There are various ways to configure the SDK, depending on your chosen framework. For Next.js and Nuxt, you can use the `@vue-storefront/next` and `@vue-storefront/nuxt` modules respectively. If you're looking for framework agnostic experience, you can use the `@vue-storefront/core` module. +:::tip +In the examples below, we assume that you have an Alokai app with the Unified Data Model. However, the approach for non-unified Alokai applications is similar. +::: + +There are various ways to configure the SDK, depending on your chosen framework. For Next.js and Nuxt, you can use the `@vue-storefront/next` and `@vue-storefront/nuxt` packages respectively. If you're looking for framework agnostic experience, you can use the `@vue-storefront/sdk` package. ::tabs{:titles='["Next.js", "Nuxt", "Other"]' class="mt-8"} @@ -27,44 +31,47 @@ npm install --save-dev @vue-storefront/next To use SDK in our application, we need to initialize it first. To do so, follow these steps: -1. Create SDK Config file - `sdk.config.ts` in root directory of your project. +1. Create an `sdk` directory in the root of your project. + +1. Create SDK Config file - `sdk.config.ts` in the `sdk` directory. ::info -It is not necessary to name the file `sdk.config.ts` specifically or to keep it in the root of your project, but it is recommended to keep it consistent with the rest of the Alokai project. +It is not necessary to name the file `sdk.config.ts` specifically or to keep it in the `sdk` directory, but it is recommended to keep it consistent with the rest of the Alokai project. :: -2. Create the SDK configuration by importing the `createSdk` function from the Next.js SDK and the modules you want to use. +2. Create the SDK configuration by importing the `createSdk` function from the Next.js SDK and using the `middlewareModule` it provides. You should also import other modules you want to use. -```ts[sdk.config.ts] -import { sapccModule } from '@vsf-enterprise/sapcc-sdk'; -import { - contentfulModule, - ContentfulModuleType, -} from "@vsf-enterprise/contentful-sdk"; +```ts [sdk.config.ts] +import { contentfulModule } from "@vsf-enterprise/contentful-sdk"; import { CreateSdkOptions, createSdk } from "@vue-storefront/next"; +import { UnifiedEndpoints } from "storefront-middleware/types"; const options: CreateSdkOptions = { - middleware: { - apiUrl: "http://localhost:4000", - } + middleware: { + apiUrl: "http://localhost:4000", + }, }; -export const { getSdk, createSdkContext } = createSdk( +export const { getSdk } = createSdk( options, - ({ buildModule, middlewareUrl, getRequestHeaders }) => ({ - commerce: buildModule(sapccModule, { + ({ buildModule, middlewareUrl, middlewareModule, getRequestHeaders }) => ({ + commerce: buildModule(middlewareModule, { apiUrl: middlewareUrl + "/commerce", + defaultRequestConfig: { + headers: getRequestHeaders(), + }, }), - cms: buildModule(contentfulModule, { + cms: buildModule(contentfulModule, { apiUrl: middlewareUrl + "/cms", }), - }), + }) ); ``` Let's break down the code above: - The `createSdk` function expects + - base SDK options including the middleware and (optionally) the multistore configuration as a first argument, - and a factory function for the SDK configuration as a second argument. Those factory function receives a context, useful for creating the SDK configuration. @@ -72,21 +79,29 @@ Let's break down the code above: - The `middlewareUrl` is the URL of the middleware instance. -- The `getRequestHeaders` function is used to retrieve the Cookie header with cookie values. +- The `middlewareModule` is an SDK module that ensures communication with the Server Middleware. It takes the `UnifiedEndpoints` type as a generic parameter. The `UnifiedEndpoints` type is a type that represents the endpoints of the Server Middleware. + +- The `getRequestHeaders` function is used to provide the incoming headers within your requests. You can use `getRequestHeaders` to access and proxy the initial cookie headers to SDK requests during SSR. Initial headers could be provided by the [`getSdk`](#getsdk) method. + Check out examples there: + - Next Pages Router [link](https://github.com/vuestorefront/vue-storefront/tree/main/packages/storefront/packages/next/__tests__/apps/pages-router) + - Next App Router [link](https://github.com/vuestorefront/vue-storefront/tree/main/packages/storefront/packages/next/__tests__/apps/app-router) + - Nuxt app [link](https://github.com/vuestorefront/vue-storefront/tree/main/packages/storefront/packages/nuxt/__tests__/app) + +::info +In the browser, `getRequestHeaders` will return an empty object. +:: -- The `createSdk` function returns - - the `getSdk` function, which is used to create the new SDK instance, - - and the `createSdkContext` function, which is used to create the SDK context, to share the same SDK instance on the Client side. +- The `createSdk` function returns the `getSdk` function, which is used to retreive the new SDK instance. ## Registering the SDK -Once you have initialized the SDK, you can register it in your application. +Once you have initialized the SDK, you can register it in your application. Alokai SDK can be used in two ways: -- **getSdk** - returns the SDK instance, which can be used to call the SDK methods directly. This is useful for server-side rendering, as it allows you to call the SDK methods directly in your application. +- `getSdk` - returns the SDK instance, which can be used to call the SDK methods directly. This is useful for server-side rendering, as it allows you to call the SDK methods directly in your application. -- **createSdkContext** - returns the SDK context, which can be used to share the same SDK instance on the Client side. This is useful for client-side rendering, as it allows you to share the same SDK instance across your application. +- `createSdkContext` - returns the SDK context, which can be used to share the same SDK instance on the Client side. This is useful for client-side rendering, as it allows you to share the same SDK instance across your application. ### getSdk @@ -95,27 +110,34 @@ Alokai SDK can be used in two ways: Below is an example of how you can use `getSdk` in your application: ```ts -import { getSdk } from "../sdk.config"; +import { getSdk } from "@/sdk/sdk.config"; const sdk = getSdk(); ``` ### createSdkContext -For client-side rendering, you can use `createSdkContext`. In order to use it, you'll need to create a new file in your application, for example `hooks/sdk.ts`: +For client-side rendering, you can use `createSdkContext`. To use it, you'll need to create a new file in your application, for example `sdk/SdkProvider.tsx`: ```ts import { createSdkContext } from "@vue-storefront/next/client"; -import { getSdk } from "../sdk.config"; +import { getSdk } from "@/sdk/sdk.config"; export const [SdkProvider, useSdk] = createSdkContext(getSdk()); ``` +To achieve easier importing, you can also create an `index.ts` file in the `sdk` directory: + +```ts [sdk/index.tsx] +export * from "./SdkProvider"; +export * from "./sdk.config"; +``` + Once you have created the SDK context, you can register it in your application. For example, if you're using the Pages Router, you can register it in `pages/_app.tsx`: ```tsx import type { AppProps } from "next/app"; -import { SdkProvider } from "../hooks"; +import { SdkProvider } from "@/sdk"; export default function App({ Component, pageProps }: AppProps) { return ( @@ -128,7 +150,7 @@ export default function App({ Component, pageProps }: AppProps) { If you're using the App Router, you can register it in `app/layout.tsx`: -```tsx[app/layout.tsx] +```tsx [app/layout.tsx] import { ReactNode } from "react"; import { Providers } from "./providers"; @@ -143,11 +165,11 @@ export default function RootLayout({ children }: { children: ReactNode }) { } ``` -```tsx[app/providers.tsx] +```tsx [app/providers.tsx] "use client"; import { ReactNode } from "react"; -import { SdkProvider } from "../hooks"; +import { SdkProvider } from "@/sdk"; export function Providers({ children }: { children: ReactNode }) { return {children}; @@ -163,8 +185,9 @@ Don't be alarmed if you see a `use client` directive in the `app/providers.tsx` Once you have registered the SDK in your application, you can start using it. Here's an example of how you can use the SAP Commerce Cloud SDK module in your application: ::code-group -```tsx[Pages Router] -import { getSdk } from "../sdk.config"; + +```tsx [Pages Router] +import { getSdk } from "@/sdk"; export function getServersideProps() { const sdk = getSdk(); @@ -177,16 +200,18 @@ export function getServersideProps() { }; } ``` -```tsx[App Router] -import { getSdk } from "../sdk.config"; + +```tsx [App Router] +import { getSdk } from "@/sdk"; const sdk = getSdk(); const { products } = await sdk.commerce.searchProduct(); ``` -```tsx[Client Side Rendering] + +```tsx [Client Side Rendering] import { useEffect, useState } from "react"; -import { useSdk } from "../hooks"; +import { useSdk } from "@/sdk"; export function ClientComponentUsingSDK() { const sdk = useSdk(); @@ -195,8 +220,8 @@ export function ClientComponentUsingSDK() { useEffect(() => { const fetchCart = async () => { const newCart = await sdk.commerce.getCart({ - cartId: '', - fields: 'code,guid,user(FULL)' + cartId: "", + fields: "code,guid,user(FULL)", }); setCart(newCart); }; @@ -205,6 +230,7 @@ export function ClientComponentUsingSDK() { }, []); } ``` + :: Code above is just an example of how you can use the SDK in your application. For more information about the avaialble methods, please refer to the respective [Integration's documentation](/integrations). @@ -215,7 +241,7 @@ That's it! You can now use VueStorefront SDK Module in your Next.js app ✨ ## Installation -To get started with the SDK within Next.js, first you have to install and configure the `@vue-storefront/nuxt` module. +To get started with the SDK within Next.js, first you have to install and configure the `@vue-storefront/nuxt` module. 1. In the root of your Storefront project run: @@ -232,7 +258,7 @@ npm install --save-dev @vue-storefront/nuxt 2. Add `@vue-storefront/nuxt` to the `modules` section of `nuxt.config.ts` -```ts[nuxt.config.ts] +```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ["@vue-storefront/nuxt"], }); @@ -242,13 +268,13 @@ export default defineNuxtConfig({ To configure the module, use `vsf` key in the Nuxt configuration object and provide necessary information such as the Middleware instance address: -```ts[nuxt.config.ts] +```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ["@vue-storefront/nuxt"], vsf: { middleware: { - apiUrl: "http://localhost:4000" - } + apiUrl: "http://localhost:4000", + }, }, }); ``` @@ -257,7 +283,7 @@ export default defineNuxtConfig({ To use SDK in our application, we need to initialize it first. To do so, follow these steps: -Create SDK Config file - `sdk.config.ts` in root directory of your project. +Create SDK Config file - `sdk.config.ts` in root directory of your project. ::info For Nuxt framework it's necessary to name the file `sdk.config.ts` and keep it in the root of your project. @@ -265,22 +291,22 @@ For Nuxt framework it's necessary to name the file `sdk.config.ts` and keep it i You should import all other SDK configuration components. See the example below illustrating the SDK configuration with SAP Commerce Cloud and Contentful modules. -```ts[sdk.config.ts] -import { sapccModule } from '@vsf-enterprise/sapcc-sdk'; -import { - contentfulModule, - ContentfulModuleType, -} from "@vsf-enterprise/contentful-sdk"; +```ts [sdk.config.ts] +import { contentfulModule } from "@vsf-enterprise/contentful-sdk"; +import { UnifiedEndpoints } from "storefront-middleware/types"; export default defineSdkConfig( - ({ buildModule, middlewareUrl, getCookieHeader }) => ({ - commerce: buildModule(sapccModule, { - apiUrl: middlewareUrl + "/commerce", + ({ buildModule, middlewareUrl, middlewareModule getRequestHeaders }) => ({ + commerce: buildModule(middlewareModule, { + apiUrl: middlewareUrl + "/commerce", // SAP Commerce Cloud integration is available at /commerce endpoint + defaultRequestConfig: { + headers: getRequestHeaders(), + }, }), - cms: buildModule(contentfulModule, { + cms: buildModule(contentfulModule, { apiUrl: middlewareUrl + "/cms", }), - }), + }) ); ``` @@ -290,16 +316,15 @@ The `defineSdkConfig` function is used for intializing the SDK. The parameter fo - the `buildModule` function, - the middleware URL (`middlewareUrl`), -- a function for retrieving the Cookie header with cookie values (`getCookieHeader`). +- the `middlewareModule` - an SDK module that ensures communication with the Server Middleware. It takes the `UnifiedEndpoints` type as a generic parameter. The `UnifiedEndpoints` type is a type that represents the endpoints of the Server Middleware, +- a function for retrieving request header, including cookie header (`getRequestHeaders`). ## Usage Once you have initialized the SDK, you can start using it. Here's an example of how you can use the SAP Commerce Cloud SDK module in your application: ```vue - +