I have a website built by Next.js and we deployed it on Vercel.
We localize our website in 5 different languages, and as I found the solution with rewrites for localizing the urls as well.
We have a custom page: customer-stories, and would like to localize these:
customer_stories: {
hu: '/esettanulmanyok',
en: '/customer-stories',
de: '/kundengeschichte',
sk: '/pribehy-zakaznikov',
cs: '/pribehy-zakazniku',
},
So in the next config file:
Inside rewrites:
{
source: '/hu/esettanulmanyok/:id*/',
destination: '/hu/customer-stories/:id*/',
locale: false,
},
{
source: '/de/kundengeschichte/:id*/',
destination: '/de/customer-stories/:id*/',
locale: false,
},
{
source: '/cs/pribehy-zakazniku/:id*/',
destination: '/cs/customer-stories/:id*/',
locale: false,
},
{
source: '/sk/pribehy-zakaznikov/:id*/',
destination: '/sk/customer-stories/:id*/',
locale: false,
},
Which is working perfectly on localhost:
http://localhost:3000/hu/esettanulmanyok/
And a dynamic id page:
http://localhost:3000/hu/esettanulmanyok/newtestwithreference/
They work as expected, but on the released site it is different:
The same url is 404: https://barion-builderio.vercel.app/hu/esettanulmanyok/
The same dynamic url is working: https://barion-builderio.vercel.app/hu/esettanulmanyok/newtestwithreference/
What is very interested, because the main page is using pagination with getInitialProps:
When I go the original page without rewrites: https://barion-builderio.vercel.app/hu/customer-stories/
And after that, I paginate on the website:
And click on the next page:
The url is changing to the correct one, but if I try to refresh on the page, again 404:
So the dynamic [id] site is working well with rewrites, but the list page with getinitialprops not working for the first time, only with the original url, and after that if the user uses the pagination the rewrites will working. On the page we use shallow routing for not loading the site again.
I don't know why is it working properly on localhost and not on the server:
Here is the full (important part) code of my customer-stories.js:
imports...
const articlesPerPage = 4
const CustomerStories = ({
stories,
texts,
tophero,
bottomhero,
enumlocalization,
page,
navbar,
footer,
subfooter,
}) => {
const { locale, pathname } = useRouter()
const [currentPage, setCurrentPage] = useState(page)
const [filteredData, setFilteredData] = useState(stories)
const router = useRouter()
const handlePageReset = () => {
console.log('reset futott')
setCurrentPage(0)
handlePaginationClick(0)
}
const {
country,
industry,
solution,
handleIndustry,
handleCountry,
handleSolution,
} = useCustomerStoriesContext()
//FILTERING ON THE SITE
useDidMountEffect(async () => {
const queryArray = {}
if (solution && solution.key != null) {
queryArray = {
...queryArray,
'data.solution.$elemMatch.solution.type': solution.key.type,
}
}
if (industry && industry.key != null) {
queryArray = {
...queryArray,
'data.industry.type.$eq': industry.key.type,
}
}
if (country && country.key != null) {
queryArray = {
...queryArray,
'data.country.type.$eq': country.key.type,
}
}
const result = await builder.getAll('barion-userstory-data', {
options: {
noTargeting: true,
query: queryArray,
page: 0,
},
omit: 'data.blocks',
})
handlePageReset()
setFilteredData((prev) => result)
}, [country, industry, solution])
const handlePaginationClick = (page) => {
//setCurrentPage(page)
router.push(
{
pathname: LOCALIZED_URLS['customer_stories'][locale],
query: page != 0 ? { page: page != 0 ? page : '' } : {},
},
undefined,
{ shallow: false }
)
}
return (
<>
<Head>
<meta name="robots" content="noindex" />
</Head>
<div>
PAGE LIST CONTENT CUSTOMER-STORIES
</div>
</>
)
}
CustomerStories.getInitialProps = async ({
req,
res,
asPath,
query,
locale,
}) => {
if (locale == 'default') {
return {
props: {
plugins: null,
texts: null,
enumlocalization: null,
integrationready: null,
},
}
}
... FETCH DATA
const page = Number(query.page) || 0
let stories = await builder.getAll('barion-userstory-data', {
options: {
noTargeting: true,
// sort: { //TODO ADD SORTING ALPHABETICALLY
// 'data.title': 1,
// },
offset: page * articlesPerPage,
},
limit: articlesPerPage * 3,
omit: 'data.blocks',
})
...
return {
stories,
tophero,
bottomhero,
texts: texts?.data.texts[locale],
enumlocalization: enumlocalization.data,
page,
navbar,
footer,
subfooter,
}
}
export default CustomerStories
Related
I have a React context I am testing that runs a single function to check for an application update. The checkForUpdate function looks like this:
async function checkForUpdate() {
if (isPlatform('capacitor')) {
const maintanenceURL =
'https://example.com/maintenance.json';
const updateURL =
'https://example.com/update.json';
try {
const maintanenceFetch: AxiosResponse<MaintanenceDataInterface> =
await axios.get(maintanenceURL);
console.log('maintain', maintanenceFetch);
if (maintanenceFetch.data.enabled) {
setUpdateMessage(maintanenceFetch.data.msg);
return;
}
const updateFetch: AxiosResponse<UpdateDataInterface> = await axios.get(
updateURL
);
console.log('updateFetch', updateFetch);
if (updateFetch.data.enabled) {
const capApp = await App.getInfo();
const capAppVersion = capApp.version;
console.log('Thi is a thinkg', capAppVersion);
if (isPlatform('android')) {
console.log('hi');
const { currentAndroid, majorMsg, minorMsg } = updateFetch.data;
const idealVersionArr = currentAndroid.split('.');
const actualVersionArr = capAppVersion.split('.');
if (idealVersionArr[0] !== actualVersionArr[0]) {
setUpdateMessage(majorMsg);
setUpdateAvailable(true);
return;
}
if (idealVersionArr[1] !== actualVersionArr[1]) {
setUpdateMessage(minorMsg);
setUpdateAvailable(true);
return;
}
} else {
const { currentIos, majorMsg, minorMsg } = updateFetch.data;
const idealVersionArr = currentIos.split('.');
const actualVersionArr = capAppVersion.split('.');
if (idealVersionArr[0] !== actualVersionArr[0]) {
setUpdateMessage(majorMsg);
setUpdateAvailable(true);
return;
}
if (idealVersionArr[1] !== actualVersionArr[1]) {
setUpdateMessage(minorMsg);
setUpdateAvailable(true);
return;
}
}
}
} catch (err) {
console.log('Error in checkForUpdate', err);
}
}
}
For some reason, in my test I wrote to test this, my axiosSpy only shows that it has been called 1 time instead of the expected 2 times. The console logs I posted for both get requests run as well. I cannot figure out what I am doing wrong.
Here is the test:
it.only('should render the update page if the fetch call to update bucket is enabled and returns a different major version', async () => {
const isPlatformSpy = jest.spyOn(ionicReact, 'isPlatform');
isPlatformSpy.mockReturnValueOnce(true).mockReturnValueOnce(true);
const appSpy = jest.spyOn(App, 'getInfo');
appSpy.mockResolvedValueOnce({
version: '0.8.0',
name: 'test',
build: '123',
id: 'r132-132',
});
const axiosSpy = jest.spyOn(axios, 'get');
axiosSpy
.mockResolvedValueOnce({
data: {
enabled: false,
msg: {
title: 'App maintenance',
msg: 'We are currently solving an issue where users cannot open the app. This should be solved by end of day 12/31/2022! Thank you for your patience 😁',
btn: 'Ok',
type: 'maintenance',
},
},
})
.mockResolvedValueOnce({
data: {
current: '1.0.0',
currentAndroid: '1.0.0',
currentIos: '2.0.0',
enabled: true,
majorMsg: {
title: 'Important App update',
msg: 'Please update your app to the latest version to continue using it. If you are on iPhone, go to the app store and search MO Gas Tax Back to update your app. The button below does not work but will in the current update!',
btn: 'Download',
type: 'major',
},
minorMsg: {
title: 'App update available',
msg: "There's a new version available, would you like to get it now?",
btn: 'Download',
type: 'minor',
},
},
});
customRender(<UpdateChild />);
expect(axiosSpy).toHaveBeenCalledTimes(2);
});
In product page, I want to get all images path that are in a specific folder and send those to client side, so I can use them in client side by passing the paths to Image component of next js. I tried this when I was developing my app via running npm run dev and it was successful. Then I pushed the changes to my GitHub repository and vercel built my app again. Now, when I go to the product page, I get an error from the server. I tried some ways to fix this problem, but I couldn't fix that. For example, I tried changing my entered path in readdir, but the problem didn't fix. Here are my codes:
const getPagePhotosAndReview = async (productName) => {
const root = process.cwd();
let notFound = false;
const allDatas = await fs
.readdir(root + `/public/about-${productName}`, { encoding: "utf8" })
.then((files) => {
const allDatas = { pageImages: [], review: null };
files.forEach((value) => {
const image = value.split(".")[0];
const imageInfos = {
src: `/about-${productName}/${value}`,
alt: productName,
};
if (Number(image)) {
allDatas.pageImages.push(imageInfos);
}
});
return allDatas;
})
.catch((reason) => (notFound = true));
if (notFound) return 404;
await fs
.readFile(root + `/public/about-${productName}/review.txt`, {
encoding: "utf-8",
})
.then((value) => {
allDatas.review = value;
})
.catch((reason) => {
allDatas.review = null;
});
return allDatas;
};
export async function getServerSideProps(context) {
if (context.params.product.length > 3) {
return { notFound: true };
}
if (context.params.product.length < 3) {
const filters = {
kinds: originKinds[context.params.product[0]] || " ",
};
if (context.params.product[1]) filters.brands = context.params.product[1];
const products = getFilteredProducts(filters, true);
if (products.datas.length === 0) {
return {
notFound: true,
};
}
return {
props: {
products: { ...products },
},
};
}
if (context.params.product.length === 3) {
const filters = {
path: context.resolvedUrl,
};
const product = getFilteredProducts(filters, false);
if (product.length === 0) {
return {
notFound: true,
};
}
const splitedPath = product[0].path.split("/");
const pagePhotosAndReview = await getPagePhotosAndReview(
splitedPath[splitedPath.length - 1]
);
if (pagePhotosAndReview === 404) return { notFound: true };
product[0] = {
...product[0],
...pagePhotosAndReview,
};
product[0].addressArray = [
textOfPaths[context.params.product[0]],
textOfPaths[context.params.product[1]],
];
return {
props: {
product: product[0],
},
};
}
}
This is the base code and I tried some ways but couldn't fix the problem. So to fix this problem, I want to ask: how can I get the name of all images in a specific directory and then use those images in client side? And errors that I get: if I go to a page directly and without going to the home of the website, I get internal server error with code of 500 and when I go to a page of my website, and then I go to my product page, I get
Application error: a client-side exception has occurred (see the browser console for more information).
And I should say that I know I should remove public from paths when I want to load an image from public folder. I did it but I still get error.
When building my Gatsby website I get the below error.
I've tried to delete and reinstall npm, update the plugins, deleting (Gatsby) cache, playing around with the siteUrl and all kinds of settings in the Gatsby config. But I can't seem to get rid of the error. The development environment works fine.
github: https://github.com/bartfluitsma/gatsby-bart-fluitsma
**Error console log**
ERROR #11321 PLUGIN
"gatsby-plugin-sitemap" threw an error while running the onPostBuild lifecycle:
`siteUrl` does not exist on `siteMetadata` in the data returned from the query.
Add this to your `siteMetadata` object inside gatsby-config.js or add this to your custom query or provide a custom `resolveSiteUrl` function.
https://www.gatsbyjs.com/plugins/gatsby-plugin-sitemap/#api-reference
47 | errors = _yield$graphql.errors;
48 | _context.next = 9;
> 49 | return Promise.resolve(resolveSiteUrl(queryRecords)).catch(function (err) {
| ^
50 | return reporter.panic(_internals.REPORTER_PREFIX + " Error resolving Site URL", err);
51 | });
52 |
File: node_modules\gatsby-plugin-sitemap\gatsby-node.js:49:36
Error: `siteUrl` does not exist on `siteMetadata` in the data returned from the query.
Add this to your `siteMetadata` object inside gatsby-config.js or add this to your custom query or provide a custom `resolveSiteUrl` function.
https://www.gatsbyjs.com/plugins/gatsby-plugin-sitemap/#api-reference
- internals.js:62 resolveSiteUrl
[gatsby-bart-fluitsma]/[gatsby-plugin-sitemap]/internals.js:62:11
- gatsby-node.js:49 _callee$
[gatsby-bart-fluitsma]/[gatsby-plugin-sitemap]/gatsby-node.js:49:36
not finished onPostBuild - 0.480s
Gatsby-config.js
module.exports = {
siteMetadata: {
title: `Bart Fluitsma`,
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
author: `#gatsbyjs`,
siteUrl: `http://bartfluitsma.com`,
},
plugins: [
`gatsby-plugin-react-helmet`,
`gatsby-plugin-image`,
'gatsby-plugin-postcss',
{
resolve: `gatsby-plugin-google-fonts-with-attributes`,
options: {
fonts: [
`montserrat\:300,400,400i,600,900`,
],
display: 'swap',
attributes: {
rel: "stylesheet preload prefetch",
},
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
}, {
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/locales`,
name: `locale`
}
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `Web development | Bart Fluitsma`,
short_name: `Bart develops`,
start_url: `/`,
background_color: `#663399`,
// This will impact how browsers show your PWA/website
// https://css-tricks.com/meta-theme-color-and-trickery/
// theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/logo-bart-fluitsma-web-design.svg`, // This path is relative to the root of the site.
},
},
{
resolve: `gatsby-plugin-react-i18next`,
options: {
localeJsonSourceName: `locale`, // name given to `gatsby-source-filesystem` plugin.
languages: [`en`, `nl`],
defaultLanguage: `en`,
// if you are using Helmet, you must include siteUrl, and make sure you add http:https
siteUrl: `https://bartfluitsma.com`,
// you can pass any i18next options
i18nextOptions: {
interpolation: {
escapeValue: false // not needed for react as it escapes by default
},
keySeparator: false,
nsSeparator: false
},
pages: [
{
matchPath: '/:lang?/blog/:uid',
getLanguageFromPath: true
},
]
}
},
// this (optional) plugin enables Progressive Web App + Offline functionality
// To learn more, visit: https://gatsby.dev/offline
// `gatsby-plugin-offline`,
{
resolve: 'gatsby-plugin-sitemap',
options: {
excludes: ['/**/404', '/**/404.html'],
query: `
{
site {
siteMetadata {
siteUrl
}
}
allSitePage(filter: {context: {i18n: {routed: {eq: false}}}}) {
edges {
node {
context {
i18n {
defaultLanguage
languages
originalPath
}
}
path
}
}
}
}
`,
serialize: ({ site, allSitePage }) => {
return allSitePage.edges.map((edge) => {
const { languages, originalPath, defaultLanguage } = edge.node.context.i18n;
const { siteUrl } = site.siteMetadata;
const url = siteUrl + originalPath;
const links = [
{ lang: defaultLanguage, url },
{ lang: 'x-default', url }
];
languages.forEach((lang) => {
if (lang === defaultLanguage) return;
links.push({ lang, url: `${siteUrl}/${lang}${originalPath}` });
});
return {
url,
changefreq: 'daily',
priority: originalPath === '/' ? 1.0 : 0.7,
links
};
});
}
}
},
],
}
The error is thrown by gatsby-plugin-sitemap. Try adding the resolveSiteUrl method in your configuration like:
const siteUrl = process.env.URL || `https://fallback.net`
resolveSiteUrl: () => siteUrl,
Applied to your code:
const siteUrl = process.env.URL || `https://fallback.net`
module.exports = {
plugins: [
{
resolve: "gatsby-plugin-sitemap",
options: {
excludes: ["/**/404", "/**/404.html"],
query: `
{
site {
siteMetadata {
siteUrl
}
}
allSitePage(filter: {context: {i18n: {routed: {eq: false}}}}) {
edges {
node {
context {
i18n {
defaultLanguage
languages
originalPath
}
}
path
}
}
}
}
`,
resolveSiteUrl: () => siteUrl,
serialize: ({ site, allSitePage }) => {
return allSitePage.edges.map((edge) => {
const { languages, originalPath, defaultLanguage } =
edge.node.context.i18n;
const { siteUrl } = site.siteMetadata;
const url = siteUrl + originalPath;
const links = [
{ lang: defaultLanguage, url },
{ lang: "x-default", url },
];
languages.forEach((lang) => {
if (lang === defaultLanguage) return;
links.push({ lang, url: `${siteUrl}/${lang}${originalPath}` });
});
return {
url,
changefreq: "daily",
priority: originalPath === "/" ? 1.0 : 0.7,
links,
};
});
},
},
},
],
};
Change the siteUrl and the fallback URL accordingly if you are not setting it in your environment file
The answer by Ferran Buireu ultimately was not the solution for OP. I had experienced the same issue and this solution would have solved his issue hadn't he abandoned it. Check this GitHub thread.
Your siteUrl issue just masks that the query was invalid, as the context is not available in gatsby >= 4 anymore, as you found out after fixing the siteUrl.
You may have used this query from the gatsby-plugin-react-i18next docs to support a sitemap.
In order to make it work, I found you have to 1. create the context yourself, and 2. adjust the queries
Create your context through gatsby-node.js (credit wilsonvolker)
/**
* Workaround for missing sitePage.context:
* Used for generating sitemap with `gatsby-plugin-react-i18next` and `gatsby-plugin-sitemap` plugins
* https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v3-to-v4/#field-sitepagecontext-is-no-longer-available-in-graphql-queries
*/
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
createTypes(`
type SitePage implements Node {
context: SitePageContext
}
type SitePageContext {
i18n: i18nContext
}
type i18nContext {
language: String,
languages: [String],
defaultLanguage: String,
originalPath: String
routed: Boolean
}
`)
}
It looks like the serialize function proposed in the i18next doesn't work as-is anymore since it apparently receives a single node, not the full graphql response. So, a few changes in gatsby-config.js to make it work again (this assumes you have a global siteUrl variable available):
query: `
{
site {
siteMetadata {
siteUrl
}
}
allSitePage(filter: {context: {i18n: {routed: {eq: false}}}}) {
nodes {
context {
i18n {
defaultLanguage
languages
originalPath
}
}
path
}
}
}
`,
serialize: (node) => {
const { languages, originalPath, defaultLanguage } = node.context.i18n
const url = siteUrl + originalPath
const links = [
{ lang: defaultLanguage, url },
{ lang: 'x-default', url },
]
languages.forEach((lang) => {
if (lang === defaultLanguage) return
links.push({ lang, url: `${siteUrl}/${lang}${originalPath}` })
})
return {
url,
changefreq: 'daily',
priority: originalPath === '/' ? 1.0 : 0.7,
links,
}
},
This worked for me without the need to create new types:
{
resolve: "gatsby-plugin-sitemap",
options: {
excludes: ["/**/404", "/**/404.html"],
resolveSiteUrl: () => siteUrl,
query: `
{
allSitePage {
edges {
node {
pageContext
}
}
}
}
`,
resolvePages: ({ allSitePage: { edges } }) => {
return edges
.filter(
({ node }) => !["/404/", "/404.html"].includes(node.pageContext.i18n.originalPath)
)
.map(({ node: { pageContext } }) => {
const { languages, originalPath, path, defaultLanguage } = pageContext.i18n;
const baseUrl = siteUrl + originalPath;
const links = [{ lang: "x-default", url: baseUrl }];
languages.forEach((lang) => {
const isDefaultLang = lang === defaultLanguage;
const isDefaultPath = path === originalPath;
const isLangSubDir = path.includes(`${lang}/`);
if (isDefaultLang && isDefaultPath) return;
if (!isDefaultLang && isLangSubDir) return;
links.push({
lang,
url: isDefaultLang ? baseUrl : `${siteUrl}/${lang}${originalPath}`,
});
});
return {
path,
url: path === "/" ? siteUrl : `${siteUrl}/${path}`,
changefreq: "daily",
priority: originalPath === "/" ? 1.0 : 0.7,
links,
};
});
},
serialize: (page) => page,
},
}
Versions:
Node: 18.13.0
"gatsby": "^5.3.3",
"gatsby-plugin-react-i18next": "^3.0.1",
"gatsby-plugin-sitemap": "^6.5.0",
Next.js enables us to define dynamic routes in our apps using the brackets [param]. It allows us to design URL in such a way that for example language is passed as parameter. When no route matches the user is redirected to error page.
The idea is simple and documentation abut dynamic routes in Next.js is rather limited. Does anybody know if it's possible to assign a default value to dynamic route parameter?
There are docs pages about i18n routing and redirects and special support for locale parameters (which I have not used personally).
In a more general sense, it sounds like what you want is optional catch all routes.
You can define a file in your foo directory with the name [[...slug]].js. The params which correspond to a path like /foo/us/en is { slug: ["us", "en"] } where each segment of the path becomes an element of the slug array.
You can use getStaticPaths to generate all of the known country/language pairs. Setting fallback: true allows for the user to enter another combo and not get a 404.
export const getStaticPaths = async () => {
return {
paths: [
{ params: { slug: ["us", "en"] } },
{ params: { slug: ["us", "es"] } },
{ params: { slug: ["ca", "en"] } },
{ params: { slug: ["ca", "fr"] } },
/*...*/
],
fallback: true, // allows unknown
};
};
As far as redirection, it depends on whether you want an actual redirect such that typing in /foo leads to /foo/us/en or if those are two separate pages which show the same content. I'm going to assume that we want an actual redirect.
You'll convert from slug to props in your getStaticProps function. This is also where you implement your redirects. I'm going to assume that you have (or can create) some utility functions like isValidCountry(country) and getDefaultLanguage(country)
export const getStaticProps = async ( context ) => {
const [country, language] = context.params?.slug ?? [];
// if there is no country, go to us/en
if (!country || !isValidCountry(country)) {
return {
redirect: {
statusCode: 301, // permanent redirect
destination: "/foo/us/en",
},
};
}
// if there is no language, go to the default for that country
if (!language || !isValidLanguage(language, country)) {
return {
redirect: {
statusCode: 301, // permanent redirect
destination: `/foo/${country}/${getDefaultLanguage(country)}`,
},
};
}
// typical case, return country and language as props
return {
props: {
country,
language,
},
};
};
There are things that you can do in the component itself with useRouter and isFallback, but I'm not sure if it's needed. In dev mode at least I'm getting proper redirects.
/foo/ca/en - ok
/foo/ca/fr - ok
/foo/ca/xx - redirects to /foo/ca/en
/foo/ca - redirects to /foo/ca/en
/foo - redirects to /foo/us/en
Complete code with TypeScript types:
import { GetStaticPaths, GetStaticProps } from "next";
export interface Props {
country: string;
language: string;
}
export default function Page({ country, language }: Props) {
return (
<div>
<h1>
{country} - {language}
</h1>
</div>
);
}
const pairs = [
["us", "en"],
["us", "es"],
["ca", "en"],
["ca", "fr"],
];
const isValidCountry = (c: string) => pairs.some(([cc]) => cc === c);
const isValidLanguage = (l: string, c: string) =>
pairs.some(([cc, ll]) => cc === c && ll === l);
const getDefaultLanguage = (c: string) =>
pairs.find(([cc]) => cc === c)?.[1] ?? "en";
export const getStaticProps: GetStaticProps<Props, { slug: string[] }> = async (
context
) => {
const [country, language] = context.params?.slug ?? [];
// if there is no country, go to us/en
if (!country || !isValidCountry(country)) {
return {
redirect: {
statusCode: 301, // permanent redirect
destination: "/foo/us/en",
},
};
}
// if there is no language, go to the default for that country
if (!language || !isValidLanguage(language, country)) {
return {
redirect: {
statusCode: 301, // permanent redirect
destination: `/foo/${country}/${getDefaultLanguage(country)}`,
},
};
}
// typical case, return country and language as props
return {
props: {
country,
language,
},
};
};
export const getStaticPaths: GetStaticPaths<{ slug: string[] }> = async () => {
return {
paths: pairs.map((slug) => ({
params: { slug },
})),
fallback: true, // allows unknown
};
};
I am trying to generate pages of each of the category's data from contentful ...
but there have some concerns.
Success
I could have got the data from contentful ..such as (title, image, and so on ) on the home page(localhost:8000)
Fail
when I clicked the read more button, Card on (localhost:8000), and switched into another page (localhost:8000/blog/tech/(content that I created on contentful)) that has not succeeded and 404pages has appeared(the only slag has changed and appeared that I hoped to).
Error
warn The GraphQL query in the non-page component "C:/Users/taiga/Github/Gatsby-new-development-blog/my-blog/src/templates/blog.js" will not
Exported queries are only executed for Page components. It's possible you're
trying to create pages in your gatsby-node.js and that's failing for some
reason.
If the failing component(s) is a regular component and not intended to be a page
component, you generally want to use a <StaticQuery> (https://gatsbyjs.org/docs/static-query)
instead of exporting a page query.
my-repo
enter link description here
gatsby.node.js
const path = require(`path`);
const makeRequest = (graphql, request) => new Promise((resolve, reject) => {
// Query for nodes to use in creating pages.
resolve(
graphql(request).then(result => {
if (result.errors) {
reject(result.errors)
}
return result;
})
)
});
// Implement the Gatsby API "createPages". This is called once the
// data layer is bootstrapped to let plugins create pages from data.
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions;
// Create pages for each blog.
const getBlog = makeRequest(graphql, `
{
allContentfulBlog (
sort: { fields: [createdAt], order: DESC }
)
edges {
node {
id
slug
}
}
}
}
`).then(result => {
result.data.allContentfulBlog.edges.forEach(({ node }) => {
createPage({
path: `blog/${node.slug}`,
component: path.resolve(`src/templates/blog.js`),
context: {
id: node.id,
},
})
})
});
const getTech = makeRequest(graphql,`
{
allContentfulBlog (
sort: { fields: [createdAt], order: DESC }
filter: {
categories: {elemMatch: {category: {eq: "tech"}}}
},)
{
edges {
node {
id
slug
}
}
}
}
`).then(result => {
const blogs = result.data.allContentfulBlog.edges
const blogsPerPage = 9
const numPages = Math.ceil(blogs.length / blogsPerPage)
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? `/category/tech` : `/category/tech/${i + 1}`,
component: path.resolve("./src/templates/tech.js"),
context: {
limit: blogsPerPage,
skip: i * blogsPerPage,
numPages,
currentPage: i + 1
},
})
})
});
return Promise.all([
getBlog,
getTech
])
};