diff --git a/docs/content/2.assistant.md b/docs/content/2.assistant.md deleted file mode 100644 index d0705d06cc..0000000000 --- a/docs/content/2.assistant.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -layout: fullscreen -navigation: false ---- - -# AI Assistant (Alpha) - - - diff --git a/docs/content/2.overview/_dir.yml b/docs/content/2.overview/_dir.yml deleted file mode 100644 index 3dff6b4ac4..0000000000 --- a/docs/content/2.overview/_dir.yml +++ /dev/null @@ -1 +0,0 @@ -title: Technical Overview diff --git a/docs/content/3.cookbook/1.index.md b/docs/content/3.cookbook/1.index.md new file mode 100644 index 0000000000..5304b72c69 --- /dev/null +++ b/docs/content/3.cookbook/1.index.md @@ -0,0 +1,10 @@ +--- +title: Available Guides +layout: default +--- + +# Cookbook + +Here you will find guides, best practices and recipes that will teach you how to achieve certain use cases in the most optimal way with Vue Storefront stack. + +:card{to="/cookbook/pass-custom-headers-and-cookies-to-requests" title="Pass Custom Headers and Cookies to Requests" description="In this tutorial, we'll demonstrate how to set up an interceptor to pass headers when making requests using the axios client inside the Vue Storefront SDK. This is useful when you need to append custom headers or cookies to your API calls."} \ No newline at end of file diff --git a/docs/content/3.cookbook/2.pass-custom-headers-and-cookies-to-requests.md b/docs/content/3.cookbook/2.pass-custom-headers-and-cookies-to-requests.md new file mode 100644 index 0000000000..35f864f9ca --- /dev/null +++ b/docs/content/3.cookbook/2.pass-custom-headers-and-cookies-to-requests.md @@ -0,0 +1,266 @@ +--- +title: Pass Custom Headers and Cookies to Requests +layout: default +scope: sdk, middleware, integrations +--- + +# Pass Custom Headers and Cookies to Requests + +In this tutorial, we'll demonstrate how to set up an interceptor to pass headers when making requests using the axios client inside the Vue Storefront SDK. This is useful when you need to append custom headers or cookies to your API calls. + +After passing the headers to the SDK client, we'll also show you how to pass them further from the Server Middleware to external services (e.g. SAP OCC API). + +## Notes +::info +This guide uses the integrations boilerplate with the nuxt option. The same basic principles apply to other frameworks, but the implementation may vary. +:: + +All SDK modules export a client instance of axios. This is the axios instance that is used to make requests to the Server Middleware. You can access this instance by importing the `client` from the SDK. + +## Steps + +step 1. +### **Import Required Modules** + +```javascript +import { initSDK, buildModule } from '@vue-storefront/sdk'; +import { client, boilerplateModule, BoilerplateModuleType } from '../../packages/sdk/src'; +import useHeaders from 'path-to-useHeaders-file'; // Replace with the correct path to the `useHeaders` composable. +``` + +step 2. +### **Define the SDK Configuration** +```javascript +const sdkConfig = { + boilerplate: buildModule(boilerplateModule, { + apiUrl: 'http://localhost:8181/boilerplate', + }), +}; +``` +step 3. +### **Fetch Headers with the useHeaders Composable** + +Use the `useHeaders` composable to fetch the necessary headers. This composable manages both server-side and client-side headers for you. + +```javascript +const { addHeadersToState, headerData } = useHeaders(); +addHeadersToState(); +``` + +step 4. +### **Setting Up Axios Interceptor** + +Before making requests using axios, establish an interceptor to automatically append the desired headers. + +#### a. **Check for Existing Interceptor** + +Check if an interceptor already exists and eject (remove) it to prevent shared headers across instances. + +```javascript +let interceptorId: number | null = null; + +if (interceptorId !== null) { + client.interceptors.request.eject(interceptorId); +} +``` + +#### b. **Define the Interceptor** + +Append custom headers to each request using the interceptor. + +```javascript +interceptorId = client.interceptors.request.use( + (config) => { + if (!config.headers) { + config.headers = {}; + } + + // Add custom headers here + config.headers['amazing-header'] = 'coolest header ever'; + config.headers['dev-mode'] = headerData.value?.isDevMode ?? 'dude'; + + return config; + }, + (error) => { + // Handle request error here + return Promise.reject(error); + } +); +``` + +step 5. +### **Initialize and Export the SDK** + +Finally, initialize the SDK with your configuration and export it. + +```javascript +return initSDK(sdkConfig); +``` + +step 6. +### **sdk.config.ts** all together + +```javascript +import { initSDK, buildModule } from '@vue-storefront/sdk'; +import { client, boilerplateModule, BoilerplateModuleType } from '../../packages/sdk/src'; + +// Maintain a reference to the interceptor +let interceptorId: number | null = null; + +export const useSdk = () => { + const sdkConfig = { + boilerplate: buildModule(boilerplateModule, { + apiUrl: 'http://localhost:8181/boilerplate', + }), + }; + + const {addHeadersToState, headerData} = useHeaders() + + addHeadersToState() + + // If an interceptor is already set, eject it to remove it + // this prevents headers being shared across instances + if (interceptorId !== null) { + client.interceptors.request.eject(interceptorId); + } + + interceptorId = client.interceptors.request.use( + (config) => { + if (!config.headers) { + config.headers = {}; + } + + // here you can set any headers you want + config.headers['amazing-header'] = 'coolest header ever'; + config.headers['dev-mode'] = headerData.value?.isDevMode ?? 'false' + + return config; + }, + (error) => { + // Do something with request error + return Promise.reject(error); + } + ); + + return initSDK(sdkConfig); +}; + +``` +::tip +This guide uses a `useHeaders` composable. This composable manages both server-side and client-side headers for you. This is just an example, you can use any method you want to fetch the headers. +:: + +step 7. +**Create a Composable to Handle Headers** + +```javascript +export default function () { + const serverHeaders = useRequestHeaders(['cookie']); + const devModeCookie = useCookie('dev-mode')?.value || ''; + + const state = useState<{ headerData: null | { isDevMode: string }, loading: boolean }>('headerState', () => ({ + headerData: null, + loading: false, + })); + + function addHeadersToState() { + getDevModeHeader(devModeCookie, serverHeaders.cookie || ''); + } + + function getHeaderObj(headers: string): { [key: string]: string } { + if (!headers) return {}; + + return headers.split(';').reduce<{ [key: string]: string }>((acc, item) => { + const parts = item.trim().split('='); + if (parts.length === 2) { + const [key, value] = parts; + acc[key] = value; + } + return acc; + }, {}); + } + + function getDevModeHeader(cookieHeader: string, serverHeader: string) { + if (process.server) { + const headerObj = getHeaderObj(serverHeader); + state.value.headerData = { isDevMode: headerObj['dev-mode'] || '' }; + return + } + + state.value.headerData = { isDevMode: cookieHeader }; + } + + return { + addHeadersToState, + ...toRefs(state.value), + } +} +``` + +## Checkpoint 1 + +You've now set up an interceptor to append custom headers to requests using the axios client in the Vue Storefront SDK. This ensures that all outgoing requests contain the headers you've defined and the Server Middleware will be able to access them through the request object. + +## Passing Headers from API Client to external services + +Now that you're passing headers from the Vue Storefront SDK client to the Server Middleware, you may want to pass them further to external services (e.g. SAP OCC API). To achieve that, you're going to create a Server Middleware extension which reads the headers from the request object and appends them as defaults to the Server Middleware client. + +## Steps +step 8. +### Creat server middleware extension + +Create a new Server Middleware extension in the `middleware.config.ts` file. In the extension, utilize the `afterCreate` hook which fires after the Server Middleware client had been created. +::warning +::warning +Do not use the below solution if: +:: +::warning +1. The headers you're passing are user-specific (e.g. they are some sort of a session token), +:: +::warning +2. The integration you're using leverages the Init Function under the hood (i.e. its Server Middleware client is created only once during the application startup). +:: +::warning +This might lead to mixing user-specific headers between requests. + +For the time being, there is no simple way to circumvent the issue. In integrations which leverage the Init Function or user-specific headers have to be set up within API Methods (where the final request to the external platform is created). +:: +:: + +```javascript +const devHeaderExtension = { + name: 'extension-dev-headers', + hooks: (req, res) => { + return { + afterCreate: ({ configuration }) => { + /** + * This adds both `devMode` and `amazingHeader` to the Server Middleware client. + */ + configuration.client.defaults.headers['dev-mode'] = req.headers['dev-mode']; + configuration.client.defaults.headers['amazing-header'] = req.headers['amazing-header']; + + return configuration; + } + }; + }, +}; + +module.exports = { + integrations: { + : { + // ... + extensions: (extensions) => [...extensions, devHeaderExtension], + } + } +}; +``` + +You can verify the extension is loading by checking the console output when you start the server. You should see something like this: +```bash +ℹ - Loading: boilerplate extension: extension-dev-headers +``` + +## Checkpoint 2 +If everything is working correctly, you should now be able to see the custom header in your external service. + +You can verify this by logging the headers in your external service, or by using a tool like HTTP Toolkit to inspect the requests. diff --git a/docs/content/3.cookbook/_dir.yml b/docs/content/3.cookbook/_dir.yml new file mode 100644 index 0000000000..c0e17f59c0 --- /dev/null +++ b/docs/content/3.cookbook/_dir.yml @@ -0,0 +1,4 @@ +title: Cookbook +sidebarRoot: true +navigation: + icon: ri:book-2-fill diff --git a/docs/content/_sidebar.json b/docs/content/_sidebar.json index 9437116c01..dca023af88 100644 --- a/docs/content/_sidebar.json +++ b/docs/content/_sidebar.json @@ -10,6 +10,12 @@ "icon": "ri:book-2-fill", "sidebarRoot": true }, + { + "title": "Cookbook", + "_path": "/cookbook", + "icon": "ri:book-2-fill", + "sidebarRoot": true + }, { "title": "SDK", "_path": "/sdk",