Skip to main content

Fetching Enterspeed-data in Next.js

For this tutorial, we assume you already have some knowledge of setting up a Next.js-project. If not, you can read the getting started guide here: https://nextjs.org/docs/getting-started.

Setting up the demo project

If you don’t wish to set up the demo project, simply skip this step. Go to GitHub (https://github.com/enterspeedhq/enterspeed-demos/tree/master/umbraco-next) and clone the project. cd into the next folder and run:
npm install
npx next dev
Create a file called .env.local and insert your environment API key generated in the Enterspeed-app under Environments like this:
.env.local
ENTERSPEED_PRODUCTION_ENVIRONMENT_API_KEY = [YOUR_ENTERSPEED_API_KEY_HERE]

// If you're using preview-mode, also insert the following:
ENTERSPEED_PREVIEW_ENVIRONMENT_API_KEY = [YOUR_ENTERSPEED_API_KEY_HERE]
ENTERSPEED_PREVIEW_SECRET = [A_SECRET_TOKEN_OF_YOUR_CHOICE]
For a production environment, your API key should be injected on build time.

Ways of fetching data

Fetching data from Enterspeed can be done in three different ways:
  • By using Handle
  • By using URL
  • By using ID
On our demo site, we’ve used handle and URL. Handle and ID are good ways of fetching data for “non-site-specific data”, e.g. the navigation. In our demo, we’re using the handle method to fetch the navigation. We’re using the URL method to fetch all of our content- and product pages. When the data is fetched you can choose to either generate it client-side, server-side, or at build time. In or demo we have used all three methods to illustrate this:
  • The navigation is generated at build time.
  • The products are generated client-side.
  • Our homepage is generated server-side.
Data can be fetched using the Fetch API or a library like Axios.
You can read more about fetching data in Next.js here: https://nextjs.org/docs/basic-features/data-fetching.
In our demo, we have made a component that fetches the data. You’ll find it in the project under “app/lib/enterspeed.js”. The components look like this:
Example: enterspeed.js
const call = async (query, preview) => {
  const url = `https://delivery.enterspeed.com/v1?${query}`;
  const response = await fetch(new Request(url), {
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": preview
        ? process.env.ENTERSPEED_PREVIEW_ENVIRONMENT_API_KEY
        : process.env.ENTERSPEED_PRODUCTION_ENVIRONMENT_API_KEY,
    },
  });

  return response.json();
};

export const getByHandle = async (handle, preview) => {
  const response = await call(`handle=${handle}`, preview);
  return response.views[handle];
};

export const getByUrl = async (url, preview) => {
  const response = await call(`url=${url}`, preview);
  return response.route;
};

How to generate content dynamically

In order to generate our content dynamically (this can be pages, blog posts, products, etc.) we need to create a component that does this for us. Luckily Next.js supports Dynamic Routes, meaning our slugs can get generated dynamically. In our demo project, we have created a file called [..slug].js. You will find it in the pages folder (you can also view it below). In the file, you’ll find the following functions:
  • getStaticPaths: Fetches the routes that need to get build.
  • getStaticProps: Fetches the data for each route that needs to get build.
  • buildPaths: Sends the navigation paths to the generateNavBarPaths function.
  • generateNavBarPaths: Generates the navigation.
  • generateSubMenu: Generates the submenus.
  • Content: Inserts the data in a JSX-component.
[...slug].js
import Entity from "../components/Entity";
import { getByHandle, getByUrl } from "../lib/enterspeed";

const paths = [];

const generateSubMenu = (children) => {
  children.forEach((child) => {
    paths.push({
      params: { slug: child.href.split("/").filter((a) => !!a) },
    });

    if (child.children) {
      generateSubMenu(child.children);
    }
  });
};

const generateNavBarPaths = (navigation) => {
  if (navigation) {
    navigation.forEach((nav) => {
      paths.push({
        params: { slug: nav.view.href.split("/").filter((a) => !!a) },
      });

      if (nav.view.children) {
        generateSubMenu(nav.view.children);
      }
    });
  }
};

const buildPaths = (navigation) => {
  generateNavBarPaths(navigation);

  return paths;
};

export async function getStaticPaths() {
  const data = await getByHandle("navigation");
  buildPaths(data.navigationItems);

  return {
    paths,
    fallback: "blocking",
  };
}

export async function getStaticProps({ params, preview }) {
  const data = await getByUrl(
    encodeURIComponent(`/${params.slug.join("/")}/`),
    preview
  );

  return {
    props: {
      view: data,
      preview: preview || null,
    },
    revalidate: 60,
  };
}

const Content = ({ view }) => {
  return <Entity view={view} />;
};

export default Content;