diff --git a/README.md b/README.md index b05366a..610593c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Changes from nuxt-ultimate-tutorial: * Attempts to improve tailwind intellisense - [Recommended extension and setting from 2022](https://stackoverflow.com/questions/47607602/how-to-add-a-tailwind-css-rule-to-css-checker#:~:text=2022%2D05%20Update,my%20recommended%20approach.) * Updated AllArticles to improve prerendered support - [Using useAsyncData to store data in application](https://nuxt.com/docs/api/composables/use-async-data) * Disables clicks in live editor - [bridge options](https://www.storyblok.com/docs/Guides/storyblok-latest-js#:~:text=If%20you%20want%20to%20prevent%20the%20iframe%20events%2C%20like%20clicking%20on%20a%20link%2C%20to%20happen%2C%20you%20can%20set%20this%20option.) +* Adds a custom async storyblok fetch composable that has proper error handling - [Throw 404 error if story was not found](https://github.com/storyblok/storyblok-nuxt/pull/267), [Adds a check for errors on useAsyncStoryblok to prevent errors from gettting to clients.](https://github.com/storyblok/storyblok-nuxt/pull/259) ## Vscode ### Recommended extensions: diff --git a/composables/useAsyncStoryblok.ts b/composables/useAsyncStoryblok.ts new file mode 100644 index 0000000..a816968 --- /dev/null +++ b/composables/useAsyncStoryblok.ts @@ -0,0 +1,43 @@ +// import { useAsyncData, useState, onMounted } from '#imports'; +import { useStoryblokApi, useStoryblokBridge } from '@storyblok/vue'; +// https://github.com/storyblok/storyblok-nuxt/pull/280 +import type { ISbStoryData, ISbError, ISbResult } from '@storyblok/vue'; + +export const useCustomAsyncStoryblok = async ( + url: string, + apiOptions = {}, + bridgeOptions = {}, +) => { + const uniqueKey = `${JSON.stringify(apiOptions)}${url}`; + // https://github.com/storyblok/storyblok-nuxt/pull/280 + const story = useState(`${uniqueKey}-state`, () => ({} as ISbStoryData)); + const storyblokApiInstance = useStoryblokApi(); + + onMounted(() => { + if (story.value && story.value.id) { + useStoryblokBridge( + story.value.id, + evStory => (story.value = evStory), + bridgeOptions, + ); + } + }); + + await useAsyncData( + `${uniqueKey}-asyncdata`, + async () => await storyblokApiInstance.get(`cdn/stories/${url}`, apiOptions), + ).then((response) => { + const { data, error } = response; + const storyblokData = data.value as ISbResult; + const storyblokError = error.value as ISbError; + if (storyblokError) { + if (storyblokError.response.status >= 400 && storyblokError.response.status < 600) { + throw new Error(storyblokError.message.message); + } + } + story.value = storyblokData.data.story; + }).catch((error) => { + console.error('error', error); + }); + return story; +}; diff --git a/pages/[...slug].vue b/pages/[...slug].vue index c16c405..8c8cb6d 100644 --- a/pages/[...slug].vue +++ b/pages/[...slug].vue @@ -20,14 +20,16 @@ const isPreview = !!(currentRoute.query._storyblok && currentRoute.query._storyblok !== ''); const version = isPreview ? 'draft' : 'published'; - await useAsyncStoryblok(currentRoute.path, { + await useCustomAsyncStoryblok(currentRoute.path, { version, language: locale.value, resolve_relations: 'popular-articles.articles', }, { preventClicks: true, }).then((res) => { - story = res.value; + if (res) { + story = res.value; + } }); } catch (error) { // eslint-disable-next-line no-console @@ -36,5 +38,5 @@