Working with images

Vue Storefront provides you with the necessary tools to display and optimize your application images - both static and the ones coming from SAP Commerce Cloud itself.

Good to know

This guide assumes you have the following files in your project:

  • ~/plugins/image-helpers.ts
  • ~/serverMiddleware/sap-image-proxy.ts

Displaying product images

Product image URLs in SAP Commerce Cloud consist of:

  • a host (e.g. api.c1jvi8hu9a-vsfspzooa2-d1-public.model-t.cc.commerce.ondemand.com)
  • the medias path parameter
  • the context query parameter (unique for every image)

An example product image URL looks like this:

https://api.c1jvi8hu9a-vsfspzooa2-d1-public.model-t.cc.commerce.ondemand.com/medias/?context=xyz

Unfortunately, when we fetch product data from SAP Commerce Cloud, the image URLs we get are just paths without the host. Therefore, we need to prepend the paths with the host ourselves. For this very reason, Vue Storefront provides you with the getSapImageUrl() helper.

Before you use it, make sure you add the missing host to your publicRuntimeConfig in nuxt.config.js:

export default {
  // ...
  publicRuntimeConfig: {
    mediaHost: 'https://your-sap-api-host.com/'
  }
};

After that, you can get the helper from the useContext composable and use it inside scripts. Alternatively, you can use it directly within your templates.

<template>
  <SfProductCard :image="$getSapImageUrl('sap-image-path')" />
</template>

<script>
import { useContext } from '@nuxtjs/composition-api';

export default {
  setup() {
    const { $getSapImageUrl } = useContext();

    const fullProductImageUrl = $getSapImageUrl('sap-image-path');
  }
}
</script>

Optimizing images

You can use external image providers to optimize your images thanks to the @nuxt/image (opens new window) module. In this example, we will show you how to use it with Cloudinary (opens new window).

Initial configuration

First, you need to register and configure the @nuxt/image (opens new window) in the nuxt.config.js file.

nuxt.config.js

export default {
  // ...
  modules: [
    '@nuxt/image'
  ],
  image: {
    provider: 'cloudinary',
    cloudinary: {
      baseURL: 'https://res.cloudinary.com/<your-cloudinary-space-name>/image/upload/',
      uploadDir: 'sap'
    }
  }
};

baseURL

A Cloudinary-specific property holding your account's image upload API URL. It might be different for different providers.

uploadDir

A property specifying the Cloudinary directory for auto-uploading SAP Commerce Cloud product images. Not required by @nuxt/image, for internal use only.

Uploading assets manually

By default, Vue Storefront leverages static assets served by Nuxt and accessible through the project's URL (e.g. logo, language icons). If you run your application locally and open up the Network tab in your browser's devtools, you will see that the URLs of your static assets start with http://localhost:3000/:

Default static assets urls

If you want these assets to be served by Cloudinary instead, upload them to your Cloudinary space manually. Bear in mind the structure of the Assets directory in Cloudinary should resemble the one in your Vue Storefront project.

Cloudinary directory structure

Auto-uploading assets

While uploading a finite number of your project's assets to Cloudinary manually might be easy, doing the same for all your product images coming from SAP Commerce Cloud would be cumbersome. To automate the process, you can leverage Cloudinary's Auto-upload (opens new window) feature.

It allows us to map a remote URL to a specific folder in our Cloudinary account. Then, whenever we access a Cloudinary image URL containing the folder prefix, the media assets are automatically retrieved from the mapped URL (unless they are present in the Cloudinary folder already).

The problem

Unfortunately, Cloudinary strips off any query parameters while accessing the mapped URL during auto-upload. It poses a problem with auto-uploading SAP Commerce Cloud product images which share the same base URL and differ only in the context query param.

Image 1

https://<your-sap-media-host>.com/medias?context=abc

Image 2

https://<your-sap-media-host>.com/medias?context=def

Therefore, Cloudinary is unable to hit the correct product image URL and will always try to reach https://<your-sap-media-host>.com/medias which results in a 400 error. Luckily, Vue Storefront provides you with two tools you can use to circumvent the issue.

The solution

First, you can leverage a global $getCloudinaryUploadPath() helper. It adds the uploadDir to the image path and transforms the context query param into a path param so that Cloudinary doesn't strip it off.

Type

function getCloudinaryUploadPath(imagePath: string): string;

Use the helper in all components dealing with SAP images you would like to auto-upload. The most common use cases include Product Detail Pages, Category Pages as well as all Cart and Order-related components.

Example

<template>
  <!--
    This SfImage component will fetch the image from
    https://res.cloudinary.com/<your-space>/image/upload/<uploadDir>/medias/context/abc.
    The cloudinary part of the url is taken from the `baseURL` property we had
    defined in @nuxt/image module configuration.
  -->
  <SfImage
    :src="cloudinaryUrl"
    image-tag="nuxt-img"
  />
</template>
<script>
import { useContext } from '@nuxtjs/composition-api';
export default {
  setup() {
    const { $getCloudinaryUploadPath } = useContext();
    
    const url = '/medias?context=abc';
    const cloudinaryUrl = $getCloudinaryUploadPath(url);

    return { cloudinaryUrl };
  }
}
</script>

Second, Vue Storefront provides you with the sap-image-proxy server middleware (opens new window). It exposes an image proxy endpoint which takes the URL transformed by the $getCloudinaryUploadPath() helper, re-creates the context query param and redirects to the final SAP image URL. To activate the endpoint, register the server middleware in nuxt.config.js:

nuxt.config.js

export default {
  // ...
  serverMiddleware: [
    {
      path: 'sap-image-proxy',
      handler: '~/serverMiddleware/sap-image-proxy.ts'
    }
  ]
}

After you had registered the endpoint, make sure Cloudinary is able to reach it. It requires either deploying (opens new window) your Vue Storefront application or running it on localhost and making it accessible from the outside by using tools such as ngrok (opens new window) or setting up port forwarding (opens new window).

Once the image proxy endpoint is globally accessible, we can map it to a specific folder in our Cloudinary account.

Cloudinary auto upload mapping

This way we make sure accessing Cloudinary URLs will auto-upload product images from SAP Commerce Cloud correctly into the desired folder in Cloudinary.

Cloudinary sap directory

Replacing img with nuxt-img in components

With all our assets uploaded to Cloudinary, the last thing left to do is replacing standard img elements with nuxt-img (opens new window) components. They are responsible for hitting the Cloudinary baseURL you had configured earlier. You can replace the tags manually:

<template>
  <SfProductCard image-tag="nuxt-img" />
</template>

or leverage the globalImgTag property provided by Vue Storefront and implemented by default. It will be equal to nuxt-img as long as you have configured @nuxt/image correctly.

<template>
  <SfProductCard :image-tag="$globalImgTag" />
</template>

After that, you should be able to see your application requesting static assets from Cloudinary:

Cloudinary final urls in devtools