How to get paths in getStaticPaths with locales in Next.js? - javascript

I'm using Strapi as a CMS, where I query for slugs, and I would like to have statically generated pages using getStaticPaths and getStaticProps in Next.js.
As I need to work with multiple locales, I have to map through the locales and get paths for each "Announcements" I'm getting from my query.
The error message I'm getting is:
Error: A required parameter (slug) was not provided as a string in getStaticPaths for /updates/announcements/[slug]
This is my getStaticPaths:
export async function getStaticPaths({ locales }: any) {
const paths = await (
await Promise.all(
locales.map(async (locale: any) => {
const { data } = await client.query({
query: gql`
query Announcements {
announcements(locale: "${locale}") {
data {
attributes {
slug
locale
}
}
}
}
`,
});
return {
announcements: data.announcements.data,
locale,
};
})
)
).reduce((acc, item) => {
item.announcements.map((p: any) => {
acc.push({
params: {
slug:
p.attributes.slug === "/" ? false : p.attributes.slug.split("/"),
},
locale: p.attributes.locale,
});
return p;
});
return acc;
}, []);
return {
paths,
fallback: false,
};
}
If I console.log(paths) I get the following in the terminal:
[
{ params: { slug: [Array] }, locale: 'en' },
{ params: { slug: [Array] }, locale: 'en' },
{ params: { slug: [Array] }, locale: 'en' },
{ params: { slug: [Array] }, locale: 'da' },
{ params: { slug: [Array] }, locale: 'sv' },
{ params: { slug: [Array] }, locale: 'nb' }
]
I might think that Next.js don't want the slug to be an array, but I'm not entirely sure. What am I doing wrong?

You page uses dynamic routes named (/updates/announcements/[slug]), therefore the param slug is required in paths.
From the Next.js getStaticPaths documentation:
The paths key determines which paths will be pre-rendered. For example, suppose that you have a page that uses Dynamic Routes named pages/posts/[id].js. If you export getStaticPaths from this page and return the following for paths:
return {
paths: [
{ params: { id: '1' }},
{
params: { id: '2' },
// with i18n configured the locale for the path can be returned as well
locale: "en",
},
],
fallback: ...
}
Then, Next.js will statically generate /posts/1 and /posts/2 during next build using the page component in pages/posts/[id].js.
The slug param can only be a string since it's used to generate routes. As you found when logging paths, you were trying to pass slug: [Array].
The problem in the question's code snippet is this expression to assign a slug:
// ...
params: {
slug: p.attributes.slug === "/" ? false : p.attributes.slug.split("/"), // 👈
},
// ...
This expression will either assign false (boolean) or an array of substrings (see the docs for String.prototype.split()).
In this case, as confirmed in a comment above, simply passing the slug as a string solves the issue.
The confusion likely came from following a tutorial that uses an optional catch-all route (pages/[[...slug]]) instead of regular dynamic routes (pages/[slug]) (ref).
From the Next.js getStaticPaths documentation again:
If the page name is pages/posts/[postId]/[commentId], then params should contain postId and commentId.
If the page name uses catch-all routes like pages/[...slug], then params should contain slug (which is an array). If this array is ['hello', 'world'], then Next.js will statically generate the page at /hello/world.
If the page uses an optional catch-all route, use null, [], undefined or false to render the root-most route. For example, if you supply slug: false for pages/[[...slug]], Next.js will statically generate the page /.

Related

Prevent getStaticPaths running for specific locale

Is it possible that you prevent Next.js getStaticPaths for generating static pages for a specific locale?
In my next config:
i18n: {
locales: ['default', 'en', 'hu', 'de', 'cz', 'eu', 'sl'],
defaultLocale: 'default',
localeDetection: true,
},
That's just because we need the locale all the time, and by the default, Next.js does not support it, so we have to use the middleware trick followed by the official Next.js docs: https://github.com/vercel/next.js/discussions/18419
But now when I want to generate sites, I don't want to generate pages like
/default/products/id1
/default/products/id2
How can I prevent Next.js doing this? Because this does not work:
For my faq page where /help/[maincategory]/[subcategory]/[help].
Using slice (I will skip the default locale):
locales.slice(1).map((locale) => {
pages.map((item) => {
item.data.mainTabList?.[locale]?.map((main, mainidx) => {
main.subTabList?.map((sub, subidx) => {
questions.map((help) => {
help.data.helplist?.[locale]?.map((title) => {
const urllink = {
maincategory: urlConverter(main.tab),
subcategory: urlConverter(sub.tab),
help: title.url
}
routes.push(urllink)
})
})
})
})
})
})
const paths = routes.map((doc) => ({
params: {
maincategory: `${doc.maincategory}`,
subcategory: `${doc.subcategory}`,
help: doc.help?.toLowerCase(),
},
}))
Can anyone help me how to solve this, that /default pages won't get generated, because that's just a hacky way for my locale prefix, we won't use it anywhere.
It's possible to control which locales getStaticPaths will generate paths for by returning the desired locales in the paths array.
From the i18n Dynamic Routing documentation:
For pages using getStaticProps with Dynamic Routes, all locale
variants of the page desired to be prerendered need to be returned
from getStaticPaths. Along with the params object returned for paths,
you can also return a locale field specifying which locale you want to
render.
In your case, your getStaticPaths function should roughly look like the following.
export const getStaticPaths = ({ locales }) => {
// Your own logic
// Filter out the `default` locale, and map through the remaining locales
locales.filter((locale) => locale !== 'default').map((locale) => {
pages.map((item) => {
item.data.mainTabList?.[locale]?.map((main, mainidx) => {
main.subTabList?.map((sub, subidx) => {
questions.map((help) => {
help.data.helplist?.[locale]?.map((title) => {
const urlLink = {
maincategory: urlConverter(main.tab),
subcategory: urlConverter(sub.tab),
help: title.url,
locale // Also push current `locale` value to be used in `paths` array
}
routes.push(urlLink)
})
})
})
})
})
})
const paths = routes.map((doc) => ({
params: {
maincategory: `${doc.maincategory}`,
subcategory: `${doc.subcategory}`,
help: doc.help?.toLowerCase(),
},
locale: doc.locale // Pass `locale` value here
}))
return {
paths,
fallback: false
}
}

How to map from API call to params in getStaticPaths?

I'm trying to map params into Next.js getStaticPaths but it doesn't work. Below you can see it works.
But it doesn't work as soon as want to add one more field which is the slug of the article.
The routing looks something like this. index>[username]>[slug] <=== slug is for article.
To simplify the code, the API looks like this.
[
{
id
username
email
},
articles: [
[Object], [Object], [Object],
]
}
]
And inside articles array looks something like this:
articles: [
{
id
title
description
slug
}
]
How do I make it work? How to map username and article's slug to param so that it works?
Edit: I want to have username slug and article slug together so that I can have www.com/[username]/[articleSlug].
I'm not sure I understood your issue very well, but you just use getStaticPaths to generate the article url like this:
export async function getStaticPaths() {
const articles = await fetchAPI("/articles")
return {
paths: articles.map((article) => ({
params: {
slug: article.slug,
},
})),
fallback: false,
}
}
Then to get the user and the article data you can use getStaticProps like this:
export async function getStaticProps({ params }) {
const article = (await fetchAPI(`/articles?slug=${params.slug}`))[0]
const users = await fetchAPI("/users")
return {
props: { article, users },
revalidate: 1,
}
}

Vuefire data not subscribing to changes

I have several different queries that I need to keep 'live' data from. When I hardcode the query it successfully shows all live changes that happen in the database. But when I pass a payload the data won't change until reloaded.
Working Query:
getOnline: firestoreAction(({ bindFirestoreRef }) => {
return bindFirestoreRef('online', db.collection('units').where('unit.on', '==', true).orderBy('info.timestamp', 'desc').orderBy('unit.status'))
}),
Not working Payload Query: gets data once
getFilterSystemId: firestoreAction(({ bindFirestoreRef} , payload) => {
return bindFirestoreRef('filterBySystemId', db.collection('units').where('unit.group', '==', payload.group).orderBy('info.timestamp', 'desc').orderBy('unit.status'))
}),
I pass the payload simply:
filterSystemId(grouphex){
let payload = {
group: grouphex.toString(),
}
this.getFilterSystemId(payload);
},
How do I pass a payload AND get live updates to any changes that happen in the database?
Ended up using vuefire instead of vuexfire and dynamically binding my queries like this.
const vuefire = db.collection('vuefire')
export default {
components: {},
data() {
return {
//view
vuefire: [],
id: 'true',
user: [],
code: 'true'
};
},
created() {
},
// firestore: {
// vuefire: db.collection('vuefire')
// }
watch: {
id: {
// call it upon creation too
immediate: true,
handler(id) {
this.$bind('user', vuefire.where('a', '==', id))
},
},
Anytime 'id' changes the dataset ('user') is recomputed and accomplishes my goal

Next.js: getStaticPaths for nested dynamic routes data structure error

I have a page [categories][price].js and im trying to achieve the data structure in getStaticPaths e.g
cat1/10 cat1/20 cat1/30 cat1/40 cat2/10 cat/20 etc
I have looked at this post: Next.js: getStaticPaths for nested dynamic routes as it's the same error but as their data structure is a bit different I'm not sure how to translate this to my example
It looks like im mapping the data incorrectly as I get the following error when trying to create my dynamic routes.
Error: Additional keys were returned from `getStaticPaths` in page "/[catSlug]/[price]". URL Parameters intended for this dynamic route must be nested under the `params` key, i.e.:
return { params: { catSlug: ..., price: ... } }
Keys that need to be moved: 0, 1.
How can I correctly map my data?
[categories][price].js
export async function getStaticPaths() {
const prices = [' 10', ' 20', ' 30']
const categories = [{ name: 'cat' }, { name: 'cat2' }, { name: 'cat3' }]
const paths = categories.map(({ slug }) =>
prices.map((price) => ({ params: { catSlug: slug, price: price } }))
)
return {
paths,
fallback: false
}
}
flatten the array
const paths = categories.map(({ name }) =>
prices.map((price) => ({ params: { catSlug: name, price: price } }))
).flat()

Elasticsearch bulk set _id

When I'm adding docs to elasticsearch with _id set I get:
Field [_id] is a metadata field and cannot be added inside a document. Use the index API request parameters.
Using client.bulk
const body = dataset.flatMap(doc => [{ index: { _index: 'myindex' } }, doc])
const { body: bulkResponse } = await client.bulk({ refresh: true, body })
I don't see a place to put the _id in the parameters.
https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html
Am I supposed to use a different method?
Thanks.
It needs to be inside the command part, but you also need to remove it from the source document in doc:
here
|
v
const body = dataset.flatMap(doc => [{ index: { _index: 'myindex', _id: doc._id } }, doc])
const { body: bulkResponse } = await client.bulk({ refresh: true, body })

Categories