Add the ability to load pages based on an API response, rather than by URL #7437
Description
What problem does this feature solve?
TL;DR
When a user visits the site, I want to be able to call an API that returns content from my CMS, and then load the relevant page component based on that response.
I do not want to sacrifice the Nuxt features like asyncData or layouts in the process.
An example might look like this:
- User visits /case-studies/case-study-one
- Nuxt calls our CMS API with the above slug
- Nuxt gets a response from the API with the relevant content and the page template to use (for this example,
{ template: "caseStudy" }
) - Nuxt routes the user to
~/pages/caseStudy.vue
I would like this functionality to be installable as a module for use in many sites.
Background
I am trying to use Nuxt (in Universal mode) with a CMS that is very flexible about the URLs for content. It is not possible for us to know what kind of content we're trying to access based on the URL. Because of this, we're currently struggling to use Nuxt with our CMS, as it is very strict about its URLs and routing.
Below are some examples of situations that can occur, which would break or otherwise not be possible with the current routing.
In most cases it's likely possible to suggest one-off workarounds, but fundamentally those workarounds won't provide the level of flexibility that our CMS users have come to expect.
Any proposed workarounds are likely to be fine for one project, but we're keen to use Nuxt to produce many websites, so the overhead of managing these restrictions becomes less tenable.
Example 1
We create a /products
route in Nuxt, which fetches content for the products
page in the CMS.
A user of the CMS then decides they want to rename this page, as they only sell books, so they call it books
. This is something that CMS users are free to do at present.
The Nuxt routes no longer reflect what is set in the CMS -- a developer would have to be involved to release a new version of the front-end, so that URLs are properly updated to say books
.
In the worst case scenario, the API call could fail if the CMS does not properly redirect requests from products
to books
following the rename. Our CMS does keep track of such renames, but it is a point of fragility that should be noted.
Example 2
In the CMS we create a hierarchical set of content that we'll call product categories, which can be infinitely nested. It is possible to put products in any category at any level. As such, we cannot tell whether a page is a product or a category based on its URL.
/products/category-1/collection-1
-- is this a product that includes the collection of all products from subcategories? Or is it a subcategory?
Currently, Nuxt would require us to flatten our URLs by moving the products to a separate, flat folder in the CMS. This is a terrible user experience for users working in the CMS, especially if there are hundreds or even thousands of products.
The above scenario can also occur with other hierarchical content that can't easily be flattened, such as microsites. It's not reasonable to ask CMS users to put all microsite pages from all microsites into one generic folder, as it becomes a disorganised mess.
Example 3
The website has a plethora of content pages at the top level, such as /about-us
, /cookie-policy
, /privacy-policy
, and so on. As the names and quantity of these are unknown, they are likely to be registered as a dynamic route.
Our CMS users then want to create a short, memorable URL for a product that they're going to be heavily marketing, say /product-one
. Currently this wouldn't work in Nuxt without some sort of special case to indicate that this URL is actually a product, so we should redirect to the product's normal URL.
Example 4
Our CMS tracks pages that have moved, and redirects the user to the new URL automatically if they visit the old one. Currently, this is not something that we'd easily be able to do with Nuxt.
Our API would need to give different responses for moved content, which Nuxt then picks up, and redirects to the new page, which will then need a new API call to fetch the content again. Ideally, we should only ever need to call our CMS' API once.
Why are we trying to use Nuxt?
We're definitely keen to modernise our front-end technology by using Nuxt, rather than continue with our current approach of using server side technology.
-
We have a small team of devs that work on a few projects, including SPAs made purely in Vue, so one language + framework for all projects reduces the amount of skills we need.
-
Nuxt and Vue are a joy to use, and so we want to use it for these projects too.
-
A lot of optimisation is taken care of for us, rather than us having to figure out how to keep up with the latest optimisation techniques in our comparatively dated CMS/backend.
-
Some of the pages we work on have complex, interactive components that are nightmarish without a front-end framework (and we want to go further down this road.)
-
We get some niceties like page transitions, easy animations, and so on.
-
Because we build a lot of sites, we try to pack up anything reusable in private packages. It's very hard to make reusable front-end components with the CMS we're using, especially keeping customisation + optimisation in mind.
We have also used Vue with our CMS to produce a few websites, by creating a separate Vue app for each template in the CMS (I believe this is acronymed as MPA in the Vue world). This worked for us, but it's definitely rough around the edges compared to going all in with an SPA and using the CMS in a fully headless role.
A few of the problems we've encountered by keeping each page a separate app:
-
Optimisation and code reuse is not a pleasant experience.
-
We had to maintain Webpack configs ourselves, which is also unpleasant.
-
Providing content from the CMS into the Vue app involved mixing two templating languages (to avoid SEO and further performance issues.)
-
Some features we want to provide involve multiple pages within the same SPA, and things like routing start to get confused.
What does the proposed changes look like?
Hopefully this has been partially explained above. Ideally a new module would be created that allows an async function to be defined somewhere, which is called prior to any routing takes place when attempting to visit any page. This async function would then be able to provide the relevant page component to load, and also commit the content to Vuex, or pass the data through as props to the component.
It is important to note that this is not the same as redirecting to pages, as that would change the destination URL. This is more about delegating the routing to the API (or indeed any other user specified code), so that the correct page can be loaded.
Whilst I appreciate that this seems to be a niche use case, I think it's likely that more people are going to want something along these lines as the popularity of headless CMS solutions increases. I think it would provide a huge amount of flexibility around routing that doesn't presently exist, and I can see the functionality being used in other ways.
My colleagues and I have spent over a week trying to implement this ourselves as a plugin or module, but we're getting nowhere fast.