How to efficiently Fetch Dynamic Routes?

31 Views Asked by At

I am creating a medium-size webshop, with a lot of dynamic data using micro services.

This is the stack I am using:

  • Nuxt 3 SSR
  • Strapi CMS (headless node-based CMS) -> linkedin with Crowdin for translations
  • Checkout, Cart, ... micro services

How can I efficiently fetch dynamic content based on user-visited URLs (slugs) in my Nuxt 3 frontend utilizing a Strapi headless backend with GraphQL for data retrieval, while maintaining clean code and optimal performance? I know how to fetch data based on slug (with GraphQL and Rest), but the way it is going right now, just doesn't feel right.

The general idea is that we work the way Apple does. Meaning, they have almost no URL depth:

URL Content type Language
https://www.apple.com/ipad/ category page /uk/
https://www.apple.com/ipad-pro/ product page /uk/
https://www.apple.com/iphone/ category page /uk/
https://www.apple.com/iphone-15-pro/ product page /uk/
https://www.apple.com/shop/buy-iphone/ shop page /uk/
https://www.apple.com/apple-one/ product page /uk/
.... .... ....

They are fetching multiple types of "content" from the 'root'. We would like to take that idea even further:

URL Content type Language
https://www.ourwebshop.com/uk/:some-category/ category page /uk/
https://www.ourwebshop.com/uk/:some-subcategory/ subcategory page /uk/
https://www.ourwebshop.com/uk/:some-bundle/ bundle page /uk/
https://www.ourwebshop.com/uk/:some-product/ product page /uk/
https://www.ourwebshop.com/uk/:some-legal/ legal page /uk/
https://www.ourwebshop.com/uk/:some-blog-category/ blog category page /uk/
https://www.ourwebshop.com/uk/:some-blog-category/:blog-post blog page /uk/
https://www.ourwebshop.com/uk/:some-product/:specifications/ specifications page /uk/
https://www.ourwebshop.com/uk/shop/:some-product/ shop page /uk/
https://www.ourwebshop.com/uk/compare/ comparison page /uk/
https://www.ourwebshop.com/nl-be/:some-product/ product page /nl-be/
https://www.ourwebshop.com/nl-be/shoppen/:some-product/ shop page /nl-be/
.... .... ....

This webshop needs to me made in at least 8 languages.


Our current approach is:

[slug]/index.vue:

<script setup lang="ts">
import CategoryPage from '~/apollo/queries/pages/categories/CategoryPage.gql'
import ProdyctPage from '~/apollo/queries/pages/products/ProductPage.gql'
import BundlePage from '~/apollo/queries/pages/bundles/BundlePage.gql'
import BlogCategoryPage from '~/apollo/queries/pages/blog/category/BlogCategoryPage.gql'
import BlogItemPage from '~/apollo/queries/pages/blog/item/BlogItemPage.gql'
//..the list goes on

const { localeProperties } = useI18n()
const slug = useRoute().params.slug;

const { data } = await useAsyncQuery(CategoryPage, {
  slug: slug,
  locale: localeProperties.value.locale_strapi,
  market: localeProperties.value.country
})

if(data == null) {
  const { data } = await useAsyncQuery(ProdyctPage, {
    slug: slug,
    locale: localeProperties.value.locale_strapi,
    market: localeProperties.value.country
  })
  if(data == null) {
    const { data } = await useAsyncQuery(BundlePage, {
      slug: slug,
      locale: localeProperties.value.locale_strapi,
      market: localeProperties.value.country
    })
    //... and so on...
  }
}
</script>


<template>
  <div class="w-screen">
    <!--Here are all the layout components-->
    <component 
      v-for="component in data.attributes.layout"
      :key="component.id"
      :is="collection[component.__typename]"
      v-bind:="component" />
    </div>
</template>

So many calls just to get the right content from Strapi, this can't be the right way of doing this. And so I thought, what if we could do one query that just asks for all the data? Here is another option:

Have a "dynamic GQL query for all possible content".

DynamicPageQuery.gql:

query SomeDynamicPagequery($locale: I18NLocaleCode, $market: String, $slug: String) {
  Products(locale: $locale, filters: { slug: { eq: $slug } }) {
    data {
      attributes {
        title
        layout {
          __typename
          ...CategorySectionDisplay
          ...HottubSectionDisplay
          ...AccessorySectionDisplay
          ...StorySectionDisplay
        }
        seo {
          meta_title
          meta_description
          keywords
          social_title
          social_description
          indexation
        }
      }
    }
  }
  Categories(locale: $locale, filters: { slug: { eq: $slug } }) {
    data {
      attributes {
        title
        layout {
          __typename
          ...CategorySectionDisplay
          ...JustASectionDisplay
          ...AccessorySectionDisplay
          ...StorySectionDisplay
        }
        seo {
          meta_title
          meta_description
          keywords
          social_title
          social_description
          indexation
        }
      }
    }
  }
  Bundles(locale: $locale, filters: { slug: { eq: $slug } }) {
    data {
      attributes {
        title
        layout {
          __typename
          ...CategorySectionDisplay
          ...HottubSectionDisplay
          ...StorySectionDisplay
        }
        seo {
          meta_title
          meta_description
          keywords
          social_title
          social_description
          indexation
        }
      }
    }
  }
  ...... and keep on querying for other dynamic data
}

[slug]/index.vue

<script setup lang="ts">
import DynamicPage from '~/apollo/queries/pages/DynamicPage.gql'

const { localeProperties } = useI18n()
const slug = useRoute().params.slug;

const { data } = await useAsyncQuery(DynamicPage, {
  slug: slug,
  locale: localeProperties.value.locale_strapi,
  market: localeProperties.value.country
})
</script>

<template>
  <div class="w-screen">
    <!--Here are all the layout components-->
    <component 
      v-for="component in data.attributes.layout"
      :key="component.id"
      :is="collection[component.__typename]"
      v-bind:="component" />
    </div>
</template>

but than again, I am querying for so many different content types. The GQL file is going to be massive, even when using fragments. And we are not talking about the duration of the call. We want the pagespeed to be as fast as possible, so this doesn't seem like an option either.

The goal is simple. Keep the frontend super clean, while maintaining performance. It would be great to just work with 1 call to Strapi. I thought about creating an endpoint in Strapi as well that matches the slug with the right content-type, but I'm not that experienced in backend yet to do it right. And since we are doing some complex querying, I just don't want to mess with the Strapi Core.

What do you think?! Is there a solution I am just not seeing yet?

0

There are 0 best solutions below