N
Nuxtā€¢5w ago
sylr

@nuxtjs/prismic high API usage

Hi, Sysadmin here, we have paid for a website which is based on nuxt and which uses primic. For a while now we have been troubled by the high prismic API usage not matching the real traffic of our website. Today I found out looking at our network logs that our nuxt application is doing batch of API calls to prismic every seconds server side ... Any user of @nuxtjs/prismic here could help me with this unwanted behavior ? Thanks. cc @Lucie - lihbr
25 Replies
kapa.ai
kapa.aiā€¢5w ago
Hi @sylr. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the information provided in the knowledge sources, I don't have enough specific details about the @nuxtjs/prismic module or its behavior to directly address your issue with high Prismic API usage. However, I can provide some general insights and suggestions based on Nuxt's data fetching mechanisms that might be relevant to your situation. 1. Data Fetching in Nuxt: Nuxt provides composables like useFetch and useAsyncData for data fetching. These are designed to fetch data only once in universal rendering scenarios, avoiding duplicate requests on both server and client sides. Nuxt Data Fetching explains this concept. 2. Caching and Key Strategy: If your application is making frequent API calls, it might be related to caching issues. As mentioned in a Stack Overflow post, adding a unique key to useFetch calls can help prevent unnecessary re-fetching: Stack Overflow - Nuxt website shows old data when refreshing 3. Server-Side Rendering Considerations: Since you're noticing server-side API calls, it's worth reviewing your server-side rendering setup. Nuxt 3 has a different approach to server middleware and hooks compared to Nuxt 2. Nuxt Migration Guide - Server provides information on these changes. 4. Third-Party Integrations: For CMS integrations like Prismic, Nuxt recommends using useAsyncData when the CMS provides its own query layer. This approach is described in the Nuxt Documentation on useAsyncData. 5. Prismic Provider in Nuxt Image: While not directly related to your API usage issue, Nuxt has a Prismic provider for image handling. This integration might be worth reviewing in your setup: Nuxt Image Prismic Provider To troubleshoot your specific issue: 1. Review your Prismic integration code, especially focusing on how and where data fetching occurs. 2. Check for any recurring server-side operations that might trigger frequent API calls. 3. Ensure you're using the latest version of @nuxtjs/prismic and check its documentation for any known issues or best practices. 4. Consider implementing proper caching strategies both on the Nuxt side and, if possible, on the Prismic side. If the issue persists, you might want to create a minimal reproduction of the problem and seek help from the Nuxt or Prismic community, as suggested in the Nuxt Bug Reporting Guide. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiā€¢5w ago
<@689882935024681017> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Lucie
Lucieā€¢5w ago
Hi @sylr, this sounds unexpected, can you share more details about your setup? Especially how you query data
sylr
sylrOPā€¢5w ago
I'm not a js developper, can you tell me more precisely what I should look for ?
Lucie
Lucieā€¢5w ago
Oh, sure! Do you have access to the site's source code? If yes, I'd like to know on your Nuxt pages, for example ~/pages/index.vue, how data from Prismic are queried, there should like be something like useAsyncData or at least prismic.client.* somewhere. If you could share what the code looks like around those that'd be helpful šŸ™‚ If you don't have access to the source code, do you notice those call spikes appearing on every page or just some ? While doing something in particular ?
sylr
sylrOPā€¢5w ago
<script lang="ts" setup>
import type { SerieDocument, SerieDocumentData, VideoDocument } from '~/.slicemachine/prismicio.d'
import { components } from '~/components/slices'

useDarkMode()
usePageTransition()

const prismic = usePrismicExtended()

const { data } = await useAsyncData(async () => {
const payload = await prismic.actions.fetchSingle<SerieDocument & { data: { episodes: VideoDocument[] } }>('serie', {
graphQuery: `{
serie {
...serieFields
}
}`,
})

const slices = await usePrismicSlicesResolver(
payload!.single.data.slices2,
prismic
)

payload!.single.data.episodes = (
await prismic.client.getByType<VideoDocument>('video', {
...prismic.options,
predicates: [prismic.predicate.at('my.video.serie', payload!.single.id)],
orderings: 'my.video.episode',
})
).results

return { ...payload, slices }
})
<script lang="ts" setup>
import type { SerieDocument, SerieDocumentData, VideoDocument } from '~/.slicemachine/prismicio.d'
import { components } from '~/components/slices'

useDarkMode()
usePageTransition()

const prismic = usePrismicExtended()

const { data } = await useAsyncData(async () => {
const payload = await prismic.actions.fetchSingle<SerieDocument & { data: { episodes: VideoDocument[] } }>('serie', {
graphQuery: `{
serie {
...serieFields
}
}`,
})

const slices = await usePrismicSlicesResolver(
payload!.single.data.slices2,
prismic
)

payload!.single.data.episodes = (
await prismic.client.getByType<VideoDocument>('video', {
...prismic.options,
predicates: [prismic.predicate.at('my.video.serie', payload!.single.id)],
orderings: 'my.video.episode',
})
).results

return { ...payload, slices }
})
The spurious calls are made server side (by the node process) I've got 10 calls to prismic made every seconds by the node process I see my browser calling for prismic client side when I browse the website, 2 calls to prismic, everything seems fine That could not explain our high prismic API usage but looking at cilium logs, I saw that my website container is also doing API calls to prismic and that I don't explain
Lucie
Lucieā€¢5w ago
Awesome, thanks! Do you have the template part of the page you shared above also?
sylr
sylrOPā€¢5w ago
<template>
<main class="pageSerie pb-48">
<SerieFeaturedCover :document="serie"></SerieFeaturedCover>
<SingleContainer :rows="serie.data.slices.length + 1">
<SingleSidebar>
<AtomDivider class="mt-0 mb-8 mobile background-[#303030]" />
<AtomSidebarEntry
:title="$t('serie.narrated')"
v-if="serie.data.narrator_copyright"
>
{{ serie.data.narrator_copyright }}
</AtomSidebarEntry>
<AtomSidebarEntry
:title="$t('serie.music')"
v-if="serie.data.narrator_copyright"
>
{{ serie.data.music_copyright }}
</AtomSidebarEntry>
<AtomSidebarEntry
:title="$t('serie.editing')"
v-if="serie.data.narrator_copyright"
>
{{ serie.data.editing_copyright }}
</AtomSidebarEntry>

<AtomSidebarEntry
class="mt-16"
:title="$t('serie.watch')"
v-if="
serie.data.youtube_external.url! ||
serie.data.vimeo_external.url ||
serie.data.instagram_external.url
"
>
<SerieWatchOnBar :document="serie" />
</AtomSidebarEntry>
<div>
<AtomSidebarEntry title="Tags" v-if="tags.length">
<div class="mt-8"></div>
<AtomTags :tags="tags" />
</AtomSidebarEntry>
</div>
<div>
<AtomSidebarEntry :title="$t('serie.share')">
<ShareActionBar class="mt-8" />
</AtomSidebarEntry>
</div>
</SingleSidebar>
<SliceZone :slices="serie.data.slices" :components="components" />
</SingleContainer>
<div class="container">
<!-- TODO types -->
<SliceZoneResolver :slices="data!.slices" />
</div>
</main>
</template>
<template>
<main class="pageSerie pb-48">
<SerieFeaturedCover :document="serie"></SerieFeaturedCover>
<SingleContainer :rows="serie.data.slices.length + 1">
<SingleSidebar>
<AtomDivider class="mt-0 mb-8 mobile background-[#303030]" />
<AtomSidebarEntry
:title="$t('serie.narrated')"
v-if="serie.data.narrator_copyright"
>
{{ serie.data.narrator_copyright }}
</AtomSidebarEntry>
<AtomSidebarEntry
:title="$t('serie.music')"
v-if="serie.data.narrator_copyright"
>
{{ serie.data.music_copyright }}
</AtomSidebarEntry>
<AtomSidebarEntry
:title="$t('serie.editing')"
v-if="serie.data.narrator_copyright"
>
{{ serie.data.editing_copyright }}
</AtomSidebarEntry>

<AtomSidebarEntry
class="mt-16"
:title="$t('serie.watch')"
v-if="
serie.data.youtube_external.url! ||
serie.data.vimeo_external.url ||
serie.data.instagram_external.url
"
>
<SerieWatchOnBar :document="serie" />
</AtomSidebarEntry>
<div>
<AtomSidebarEntry title="Tags" v-if="tags.length">
<div class="mt-8"></div>
<AtomTags :tags="tags" />
</AtomSidebarEntry>
</div>
<div>
<AtomSidebarEntry :title="$t('serie.share')">
<ShareActionBar class="mt-8" />
</AtomSidebarEntry>
</div>
</SingleSidebar>
<SliceZone :slices="serie.data.slices" :components="components" />
</SingleContainer>
<div class="container">
<!-- TODO types -->
<SliceZoneResolver :slices="data!.slices" />
</div>
</main>
</template>
Lucie
Lucieā€¢5w ago
The code above looks fine, I see it making at least 2 Prismic calls (one fetchSingle and one getByType), and potentially more within the usePrismicsliceResolver āœ… template is alright (just wanted to check if it was multiroot (which is not supported by Nuxt) or not) Are you able to locate the code behind usePrismicSliceResolver perhaps? It could help I think
sylr
sylrOPā€¢5w ago
export function usePrismicSliceResolver(props, resolve, prismic) {
const resolved = ref()
const loaded = ref(!!props.payload)
try {
if (props.payload) {
resolved.value = props.payload
} else {
onMounted(async () => {
resolved.value = await resolve(props.slice, prismic)
loaded.value = true
})
}
} catch (e) {
console.log('--------ERROR PRISMIC SLICE RESOLVER-------', e)
showError({
statusCode: 404,
statusMessage: `Cannot found`,
})
}

return {
loaded,
resolved,
}
}
export function usePrismicSliceResolver(props, resolve, prismic) {
const resolved = ref()
const loaded = ref(!!props.payload)
try {
if (props.payload) {
resolved.value = props.payload
} else {
onMounted(async () => {
resolved.value = await resolve(props.slice, prismic)
loaded.value = true
})
}
} catch (e) {
console.log('--------ERROR PRISMIC SLICE RESOLVER-------', e)
showError({
statusCode: 404,
statusMessage: `Cannot found`,
})
}

return {
loaded,
resolved,
}
}
Lucie
Lucieā€¢5w ago
Thanks! I'm not sure this is the one though šŸ¤” (this function requires 3 arguments while the one in the first snippet your shared only use 2, and I don't think the usage is the same, but maybe I'm really wrong) But I think this function or its resolvers might be making a lot of Prismic calls for some reasons
sylr
sylrOPā€¢5w ago
/**
* Use in asyncData to handle slices resolving
*/
export async function usePrismicSlicesResolver(slices, prismic) {
try {
if (!slices) return []
return await Promise.all(
slices
.filter((slice) => {
return slice.primary || slice.items
})
.map(async (slice) => {
const component = components[slice.slice_type]
let payload = null
if (component.resolve) {
payload = await component.resolve(slice, prismic)
}

return {
slice,
payload,
}
})
)
} catch (e) {
console.log('--------ERROR PRISMIC SLICES RESOLVER-------', e)
showError({
statusCode: 404,
statusMessage: `Cannot found`,
})
}
}
/**
* Use in asyncData to handle slices resolving
*/
export async function usePrismicSlicesResolver(slices, prismic) {
try {
if (!slices) return []
return await Promise.all(
slices
.filter((slice) => {
return slice.primary || slice.items
})
.map(async (slice) => {
const component = components[slice.slice_type]
let payload = null
if (component.resolve) {
payload = await component.resolve(slice, prismic)
}

return {
slice,
payload,
}
})
)
} catch (e) {
console.log('--------ERROR PRISMIC SLICES RESOLVER-------', e)
showError({
statusCode: 404,
statusMessage: `Cannot found`,
})
}
}
I got a response from prismic support the loop calls are for /api/v2/documents/search Ok, so for every call I make with curl to my website index, the node process makes 8 calls to prismic server side ...
Lucie
Lucieā€¢5w ago
OK, yes! This sounds all possible then with the setup you shared to me (usePrismicSlicesResolver might make a few extra calls based on resources needed by your slices that your developers implemented). With that in mind I don't think there is anything wrong with your code (like crazy process loops or else), and making ~8 calls to fetch the resources to render your pages sounds alright and typical when augmenting your slices content with extra data. Those calls are also made concurrently which is great to improve your website's speed. I wouldn't be surprised then if you see your API usage being ~10x your actual website traffic and our API should handle the load without issues (most of them should be cache hit on Cloudflare sitting in front of our API) If API usage becomes a concerns of your, you could look into Nuxt's hybrig rendering feature (https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering) which could allow you to cache or make static some pages that aren't so dynamic, preventing you from querying Prismic on every visit
sylr
sylrOPā€¢5w ago
It's not 10x, Prismic tells me I do 7millions calls a month, my analytics tells me my websites has had 382 pageviews for the last 30 days šŸ˜­
Lucie
Lucieā€¢5w ago
OK, there's definitely something to worry about and worth investigating then, my bad!
sylr
sylrOPā€¢5w ago
I have probes that curl my webistes index though, there are not counted by the analytics because they do not execute javascritp but since the calls are made server side my probes are generated calls to prismic nonetheless does @nuxtjs/sprismic has some sort of caching feature I could use to avoid calling the API every time ?
Lucie
Lucieā€¢5w ago
Prismic API's cached on Cloudflare CDN and invalidated when new content is published on Prismic. If you want to cache results on Nuxt's end I would look into Hybrid Rendering as suggested above: https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering, @nuxtjs/prismic itself doesn't provide another caching mechanism as those often don't play well with content update
sylr
sylrOPā€¢5w ago
Ok, thank you very much @Lucie - lihbr
Lucie
Lucieā€¢5w ago
On the other hand, I would dig into the 7 million calls thing because that sounds really weird to me, maybe it's not the homepage or some other page that burst many calls(?)
sylr
sylrOPā€¢5w ago
Yeah, unfortunately I know nothing about web developpement and we don't have support anymore from the studio that made the site šŸ˜¦
Lucie
Lucieā€¢5w ago
Would you like me to have a look at your source code (if you're comfortable sharing it)? Happy to
sylr
sylrOPā€¢5w ago
if you don't mind I can ask if my cto is comfortable
Lucie
Lucieā€¢5w ago
Sure thing, let me know! You can either share a zip at [email protected], or invite me temporarily on your GitHub (if using GitHub) with @lihbr
sylr
sylrOPā€¢5w ago
Will do! Thank lucie!
Lucie
Lucieā€¢5w ago
Got the invite, I'll have a look at it during the afternoon šŸ™‚

Did you find this page helpful?