< Back to blog

Building faster headless eCommerce applications

Next SEO

When you set out to build a new headless commerce project, you’ll first need to consider the requirements of your project and choose a suitable set of technologies.

One of the benefits of choosing Commerce.js for eCommerce applications is that, in many cases, you don’t need a backend for your website or app. You can simply build your frontend and power the whole thing with API-driven services.

Headless Commerce APIs

Once you’ve decided on your stack, you’ll need to decide how best to implement the services you’ve chosen.

Here are a couple of tips for getting the best performance out of your web applications:

Use a framework that provides static site generation (SSG)

Frameworks like React and Vue.js are used more and more to build modern web experiences, however neither give you the ability to statically generate your website. Static generation means that you’ll essentially pull in all of the information you need to build the static parts of your website once, rather than every time somebody visits your app.

Some examples of things that can be statically generated are product category and product detail pages, pages with CMS content, marketing landing pages, etc.

Why SSG is good for performance?

Fewer API calls mean a faster browsing experience for your users. As well as saving time for loading content, another benefit of using static generation is that you can deploy your entire website to a globally distributed content delivery network (CDN). This means customers anywhere in the world will get the best experience possible from loading the static part of your application.

The good news is that both React and Vue.js have popular and well maintained wrapper libraries that do provide support for this. For React, check out Gatsby or Next.js, and for Vue have a look at Nuxt. There are also framework agnostic SSG’s like 11ty, Hugo, and Jekyll.

Let’s look at the data

To illustrate this point a little, here’s a quick table showing the number of API calls executed as your application scales. In this scenario, a user visits your website, looks at 4 different category pages, 3 different product pages, then adds 2 different products to cart. Note that the two API calls in this example under SSG reflect one “list categories” call, and one “list products” call.

Users API calls (without SSG) API calls (during SSG) API calls (on frontend)
1 9 2 2
10 90 2 20
100 900 2 200
1,000 9,000 2 2,000
10,000 90,000 2 20,000

As you can see, API consumption scales rapidly as your website traffic increases. Performance benefits aside, without SSG your application may get rate limited by the API providers you are using at a certain threshold (e.g. 200 requests per minute), which means nobody will be able to use your application until the rate limit is reset.

An example of a product display page in Next.js set up to use static site generation might obtain its data like this:

// File: pages/products/[permalink].js
export async function getStaticProps({ params }) {
  const { permalink } = params;

  const product = await commerce.products.retrieve(permalink, {
    type: 'permalink',
  });

  return {
    props: {
      product,
    },
  };
}

function ProductPage({ product }) {
  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.price.formatted_with_symbol}</p>
      <div dangerouslySetInnerHTML={{ __html: product.description }} />
    </div>
  );
}

export default ProductPage;

This code snippet will build a page for each of your products using the product’s permalink from the route as the unique identifier. You would need to build a category/product list page, which loads all of your products using commerce.products.list() and creates a link to each in order to connect the dots here. For example:

// File: pages/products.js
import Link from 'next/link';

export async function getStaticProps() {
  const { data } = await commerce.products.list();

  return {
    props: {
      products,
    },
  };
}

function Products({ products }) {
  return (
    <div>
      <h1>My products</h1>
      <ul>
        {products.map((product) => (
          <li key={product.id}>
            <Link href={`/products/${product.permalink}`}>
              <a>{product.name}</a>
            </Link>
          </li>
        )}
      </ul>
    </div>
  );
}

export default Products;

When you build your website this way, next generate will instruct Next.js to build static HTML versions of pages that use this strategy, resulting in faster page load times and fewer API calls in the browser at runtime.

Improving API performance for dynamic requests

There will be certain parts of your application that need to be dynamic, such as adding a product to cart, checking for stock availability, logging a customer in, etc. While this can’t be avoided, it can be improved slightly by using local proxies. Next.js is a framework that has out-of-the-box support for this, they call it “API routes”. You can read more about why we love Next.js here.

There are a number of benefits to using server side API proxies, such as:

  • Removing the need for OPTIONS requests in cross-origin XHR requests
  • Hiding the underlying APIs you consume
  • Add your own logic, or secret key API consumption
  • Define your own HTTP caching rules

When things change in Commerce.js, for example when you add or delete a product, you can use our webhooks to invalidate caches, or rebuild your website.

Use an image optimisation CDN

When you upload images to Chec/Commerce.js, we serve them back to you in the format you uploaded them in via our content delivery network (CDN). If you’ve optimised your images beforehand, this is probably fine, but if you want to get the most out of your product images you may want to use an image optimisation CDN such as Cloudinary or Imgix.

Both Cloudinary and Imgix offer URL-based APIs to manipulate your images, so you can do things like adding focus points, cropping, changing orientation, adding a watermark, changing colour profiles etc, all from the query string of your image URL. On top of this, they’ll also give your customers a version of your image that is optimised specifically for the device they’re using to access your website.

Next.js comes with an Image component built for image optimisation, which also supports a number of cloud providers such as Cloudinary and Imgix. A quick example of using this to render your product images in a Next.js template might look like this:

// File: pages/products/[permalink].js
import Image from 'next/image';

function ProductPage({ product }) {
  return (
    <div>
      <h1>{product.name}</h1>
      <div>
        {product.assets.map((asset) => (
          <Image
            key={asset.id}
            src={assets.url}
            alt={asset.description}
            width={asset.image_dimensions.width}
            height={asset.image_dimensions.width}
          />
        )}
      </div>
    </div>
  );
}

To enable Imgix, you’ll need to configure the loader for it:

// File: next.config.js
module.exports = {
  images: {
    loader: 'imgix',
    path: 'https://example.com/myaccount/',
  },
};

You can take advantage of the Next.js Image component using the Commerce.js CDN out of the box, without needing a cloud image CDN. Here’s what the config for that might look like:

// File: next.config.js
module.exports = {
  images: {
    domains: ['cdn.chec.io'],
  },
};

Commerce.js asset object responses contain the necessary metadata to use with your Next.js Image components (such as the dimensions and alternative text), so it’s an easy drop in replacement for using <img /> tags.

Resources and Community

Check out our documentation and community articles.

Join our community on Slack to connect with eCommerce developers from around the world. Share your projects with us, ask questions, or just hang out!

Join our community
Dev community image