How to generate routes for Nuxt with Storyblok?
Nuxt
Nuxt v3 allows you to generate your website statically (SSG) by prerendering all your routes based on your page
directory, but since we are fetching the stories from the Storyblok API, we need to tell Nuxt which routes we want to pre-render.
For that, Nuxt uses a new server engine Nitro that provides us a really useful hook nitro:config
where we are going to fetch our routes and tell Nitro to prerender them.
We are going to use the fetch
API and the Storyblok CDN API v2.
NOTE: To use
fetch
you need Node v18 or higher.
// nuxt.config.ts import { fetchStories } from './fetchStories.ts' export default defineNuxtConfig({ hooks: { async 'nitro:config'(nitroConfig) { if (!nitroConfig || nitroConfig.dev) { return } const token = process.env.STORYBLOK_TOKEN let cache_version = 0 // other routes that are not in Storyblok with their slug. let routes = ['/'] // adds home directly but with / instead of /home try { const result = await fetch(`https://api.storyblok.com/v2/cdn/spaces/me?token=${token}`) if (!result.ok) { throw new Error('Could not fetch Storyblok data') } // timestamp of latest publish const space = await result.json() cache_version = space.space.version // Recursively fetch all routes and set them to the routes array await fetchStories(routes, cache_version) // Adds the routes to the prerenderer nitroConfig.prerender.routes.push(...routes) } catch (error) { console.error(error) } }, }, })
We move the fetching of the stories to an outside function called fetchStories
in case we need pagination (For spaces with +100 Stories, see API limit here)
async function fetchStories(routes: string[], cacheVersion: number, page: number = 1) { const token = process.env.STORYBLOK_TOKEN const version = 'published' const perPage = 100 const toIgnore = ['home', 'en/settings'] try { const response = await fetch( `https://api.storyblok.com/v2/cdn/links?token=${token}&version=${version}&per_page=${perPage}&page=${page}&cv=${cacheVersion}`, ) const data = await response.json() // Add routes to the array Object.values(data.links).forEach(link => { if (!toIgnore.includes(link.slug)) { routes.push('/' + link.slug) } }) // Check if there are more pages with links const total = response.headers.get('total') const maxPage = Math.ceil(total / perPage) if (maxPage > page) { await fetchStories(routes, cacheVersion, ++page) } } catch (error) { console.error(error) } }
Nuxt v2
Nuxt.js allows you to export your application as a statically generated website. To do so it will need to know every route available. You can use one of the following two examples to generate
your routes for Nuxt.js using Storyblok.
import axios from 'axios' export default { generate: { routes: function (callback) { const token = process.env.STORYBLOK_TOKEN const version = 'draft' const perPage = 2 let cacheVersion = 0 const page = 1 const toIgnore = ['home', 'en/settings'] // other routes that are not in Storyblok with their slug. const routes = ['/'] // adds / directly // Load space and receive latest cache version key to improve performance axios.get(`https://api.storyblok.com/v2/cdn/spaces/me?token=${token}`).then((space) => { // timestamp of latest publish cacheVersion = space.data.space.version // Call for all Links using the Links API: https://www.storyblok.com/docs/Delivery-Api/Links axios.get(`https://api.storyblok.com/v2/cdn/links?token=${token}&version=${version}&cv=${cacheVersion}&per_page=${perPage}&page=${page}`).then((res) => { Object.keys(res.data.links).forEach((key) => { if (!toIgnore.includes(res.data.links[key].slug)) { routes.push('/' + res.data.links[key].slug) } }) // Check if there are more pages available otherwise execute callback with current routes. const total = res.headers.total const maxPage = Math.ceil(total / perPage) if (maxPage <= 1) { callback(null, routes) return; } // Since we know the total we now can pregenerate all requests we need to get all stories let contentRequests = [] for (let page = 2; page <= maxPage; page++) { contentRequests.push(axios.get(`https://api.storyblok.com/v1/cdn/links?token=${token}&version=${version}&per_page=${per_page}&page=${page}`)) } // Axios allows us to exectue all requests using axios.spread we will than generate our routes and execute the callback axios.all(contentRequests).then(axios.spread((...responses) => { responses.forEach((response) => { Object.keys(response.data.links).forEach((key) => { if (!toIgnore.includes(res.data.links[key].slug)) { routes.push('/' + res.data.links[key].slug) } }) }) callback(null, routes) })).catch(callback) }) }) } }, }