I am trying to request a single entry from contentful CMS with the getEntry() method. I keep getting a 404. I really don't understand what am I doing wrong.
export async function getStaticProps({ params }) {
const p = params.id;
const client = contentful.createClient({
space: process.env.CONTENTFUL_SPACE_ID,
environment: process.env.CONTENTFUL_ENVIRONMENT_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});
const entry = await client.getEntry(p);
return { props: { entry: entry } };
}
I created a small project to replicate the issue, but I couldn't. It works fine for me. This is what my sample code looks like:
import {createClient} from 'contentful'
export default function Comp() {
return (
<div>
Hello
</div>
)
}
export async function getStaticProps({ params }) {
const p = params.id;
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID,
environment: process.env.CONTENTFUL_ENVIRONMENT_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});
const entry = await client.getEntry(p);
return { props: { entry: entry } };
}
export async function getStaticPaths() {
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID,
environment: process.env.CONTENTFUL_ENVIRONMENT_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});
const entry = await client.getEntries({
content_type: "blogPost"
})
const paths = entry.items.map(item => {return {params: {id: item.sys.id}}});
return { paths, fallback:true}
}
You might be getting a 404 because the paths might be correctly configured. If the above code sample doesn't help, feel free to link the repo or share more details :)
Related
I use Next JS in my project. I want do only one request in page. And in next docs says that using get Static Props is that's what I need. But in doesnt work for me.
This is my code
export async function getStaticPaths() {
const leaguesCol = await collection(database, 'leagues');
const leaguesSnapshot = await getDocs(leaguesCol);
const leagues = leaguesSnapshot.docs.map(doc => doc.data());
return {
paths: leagues.map((item) => ({
params: {
id: item.link,
},
})),
fallback: false,
};
}
export async function getStaticProps() {
const { id } = context.params;
const leaguesRef = await collection(database, 'highlights');
const q = query(leaguesRef, where('league', '==', id));
const leagueSnapshot = await getDocs(q);
const data = leagueSnapshot.docs.map(doc => doc.data());
return {
props: { data },
};
}
But, when i deploy project in Firebase, i see that request happens in every routing to page. For example, in screen my routing between "spain" and "germany" pages
enter image description here
I want to build my next js project in which i am using
https://www.npmjs.com/package/#react-oauth/google
but when I build it i get the following :
this is layout.js and in _app.js I have all the components wrapped in GoogleOAuthProvider
import { GoogleLogin } from '#react-oauth/google';
import {FcGoogle} from "react-icons/Fc"
import { useGoogleLogin } from '#react-oauth/google';
export default function Layout({ children }) {
const client_id = ""
const responseGoogle = (response) => {
console.log(response);
}
CUTTED (NOT RELEVANT)
const login = useGoogleLogin({
onSuccess: codeResponse => {
const { code } = codeResponse;
console.log(codeResponse)
axios.post("http://localhost:8080/api/create-tokens", { code }).then(response => {
const { res, tokens } = response.data;
const refresh_token = tokens["refresh_token"];
const db = getFirestore(app)
updateDoc(doc(db, 'links', handle), {
refresh_token : refresh_token
})
updateDoc(doc(db, 'users', useruid), {
refresh_token : refresh_token
}).then(
CUTTED (NOT RELEVANT)
)
}).catch(err => {
console.log(err.message);
})
},
onError: errorResponse => console.log(errorResponse),
flow: "auth-code",
scope: "https://www.googleapis.com/auth/calendar"
});
return (
<>
CUTTED (NOT RELEVANT)
</>
)
}
Everything works perfect in dev mode but it does not want to build
I've faced this issue too. So I use 'GoogleLogin' instead of 'useGoogleLogin', then you can custom POST method on 'onSuccess' property.
import { GoogleLogin, GoogleOAuthenProvider} from '#react-oauth/google';
return(
<GoogleOAuthProvider clientId="YOUR CLIENT ID">
<GoogleLogin
onSuccess={handleLogin}
/>
</GoogleOAuthProvider>
The async function will be like...
const handleLogin = async = (credentialResponse) => {
var obj = jwt_decode(credentialResponse.credential);
var data = JSON.stringify(obj);
console.log(data);
const data = {your data to send to server};
const config = {
method: 'POST',
url: 'your backend server or endpoint',
headers: {},
data: data
}
await axios(config)
}
Spending whole day, this solve me out. Just want to share.
You have to wrap your application within GoogleOAuthProvider component. Please keep in mind that you will need your client ID for this.
import { GoogleOAuthProvider } from '#react-oauth/google';
<GoogleOAuthProvider clientId="<your_client_id>">
<SomeComponent />
...
<GoogleLoginButton onClick={handleGoogleLogin}/>
</GoogleOAuthProvider>;
I am trying to generate my blog details page but next keeps giving me the 404 page. I am fetching the data from a headless cms called contentful. Here is what my file structure and code looks like
And this is my [path]s.js page
import {createClient} from "contentful"
const client = createClient({
space: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
accessToken: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
});
export const getStaticPaths = async () => {
const res = await client.getEntries({
content_type: "blog"
})
const paths = res.items.map(item => {
return {
params: {slug: item.fields.slug}
}
})
return {
paths,
fallback: false
}
}
export const getStaticProps = async ({params}) => {
const {items} = await client.getEntries({
content_type: "blog",
"fields.slug": params.slug
})
return {
props: {blog: items[0]}
}
}
function blogDetails({blog}) {
console.log(blog)
return (
<div>
Blog details
</div>
)
}
export default blogDetails
I do not understand where I am going wrong. What could be my problem?.
Firstly, you cannot use the square brackets as a "wildcard" at the beginning of the path and add some other characters after. So you should rename your file as just [path].js (without the s after [path]).
Secondly, in your getStaticPaths function, when you return the object of params inside paths, name the keys as the dynamic path you've named your file as. In your case, the object you return should be params: {path: item.fields.slug}. Alternatively, you can rename your file as [slug].js and leave the code like it is.
// [path].js
export const getStaticPaths = async () => {
const res = await client.getEntries({
content_type: "blog"
})
const paths = res.items.map(item => {
return {
params: {path: item.fields.slug} //renamed the key as "path"
}
})
return {
paths,
fallback: false
}
}
You can read more about dynamic routes and getStaticPaths() in the official documentation.
So I have this blog, with dynamic url for posts (blog/[postId]). Posts are being added through keystone.js cms. It worked fine via CSR, but i came to a necessity to add proper meta open graph tags for each post (for correct share snippets).
I added getStaticProps and getStaticPaths to my post page, and now my meta tags are working fine, but now i get 404 when trying to access any post, that was added after build. Meaning, now i have to rebuild after each new post. My question is - is it possible to set Next to render component as CSR, if it fails to find path for it with getStaticPaths? I've tried to use try catch, but that didn't work. Now i came up with this concept of using container, but it does not seem to be the right way to use fallback. Or is it? I am fairly new to Next, so i am a bit confused.
const PostContainer = (props) => {
const [postData, setPostData] = React.useState(null);
const router = useRouter();
React.useEffect(() => {
if (router.query?.id) {
getPost();
}
}, [router.query]);
const getPost = async () => {
const postData = await getPost(`${router.query.id}`);
if (postData) {
setPostData(postData);
}
};
if (router.isFallback) {
return <Post data={postData}
metaProps={defaultMetaProps}
/>
}
return (
<Post data={postData}
metaProps={props}
/>
);
};
export const getStaticPaths = async () => {
const list = await getPosts();
if (list?.length) {
const paths = list.map((post) => {
return {
params: {
id: post.id,
},
};
});
return {
paths,
fallback: true,
};
}
return {
paths: [],
fallback: true,
};
};
export const getStaticProps = async ({ params }) => {
const { id } = params;
const post = await getPost(id);
const {title, description, previewImage} = post
const props = {
props: {
title,
description,
url: `https://sitename./blog/${id}`,
image: previewImage,
},
revalidate: 1,
};
return props
};
export default PostContainer;
And one more thing - do i understand correctly that if fallback option is true, it takes only one visit to the page by ANY user, after which new path is generated on the server permanently and is accessible for anyone who comes next, therefore fallback always fires just once for this first visit of first user?
I have set up exportPathMap but I get an empty object when exporting getStaticProps from my component. I don't understand how I'm supposed to access it?
// next.config.js
exportPathMap: async function (
defaultMapPath,
{ dev, dir, outDir, distDir, buildId }
) {
const paths = {}
const response = await fetch(...)
const data = await response.json()
data.forEach((item) => {
paths[`/item/${item.id}`] = {
page: '/itemPage',
query: {
item,
},
}
})
return paths
},
And
// itemPage.js
...
export async function getStaticProps(props) {
console.log('props', props) // returns "props {}""
return {
props: props
}
}
...
It's difficult to find it specifically stated in the documentation right now, but here are your options.
OPTION 1
Use getStaticPaths and getStaticProps like so (from "with-static-export" in the nextJS examples)
// [id].js post file
export async function getStaticPaths() {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts?_page=1'
)
const postList = await response.json()
return {
paths: postList.map(post => {
return {
params: {
id: `${post.id}`,
},
}
}),
fallback: false,
}
}
export async function getStaticProps({ params }) {
// fetch single post detail
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${params.id}`
)
const post = await response.json()
return {
props: post,
}
}
OPTION 2
Use exportPathMap AND getInitialProps
// item.js
...
Page.getInitialProps = function (context) {
return context.query.item
}
// next.config.js
module.exports = {
// ...Same as in your question...
}
Both will allow you to have static optimization. With Option 1, you need not export, next build is sufficient. For Option 2 you will need to run next export
From: https://nextjs.org/docs/advanced-features/static-html-export
If your pages don't have getInitialProps you may not need next export at all; next build is already enough thanks to Automatic Static Optimization.
Another note: You can use both methods inside a single project.