I have an application with React as client framework and Graphql for running the API.
Plan is fetching user posts and posts count.
Client side
const Profile = () => (
<div>
<PostCount />
<Posts />
</div>
)
const PostCount = () => ...
const Posts = () => ...
We need to display posts in Posts component and the posts count in Count component.
So my question is, which is better.
fetch all of data in Profile component in one request and send it to Post and Count component as props. or fetch posts count in Count component and posts in Post component.
scenario one includes one request to server and bigger chunk of data.
scenario two includes two request to server and smaller chunk of data.
Scenario one server side:
const schema = `
type Query {
feeds(): Feed!
}
type Feed {
posts: [Post!]!
count: Int!
}
type Post {
...
}
`
async function feed() {
const posts: await Post.findAll();
const count = await Post.count();
return {
count
posts,
}
}
Scenario two server side:
const schema = `
type Query {
posts(): [Post!]!
count(): Int!
}
type Post {
...
}
`
async function feed() {
const posts: await Post.findAll();
return posts;
}
async function count() {
const count = await Post.count();
return count;
}
P.S. also consider bigger data. posts and counts are example. for example user posts and user comments.
Both ways are correct! it depends on your application to choose a better approach!
count is usually faster than fetching data ( someone might mess it up!:D), so if you fetch it separately, you can show it faster while your posts are still in loading.
BTW, GQL handles Promise by itself! so there's no need for that async awaits!
Related
Problem:
An entire field of my MongoDB's collections' is not transmitted correctly from the db to the react client. For an exemple, the first element of this field is tweet_id: 1537466989966413825 in my DB. It becomes tweet_id: 1537466989966413800 when I fetch from my client.
My steps
I pull my data from the MongoDB using their App services' function like that:
exports = function(){
var collection = context.services.get("mongodb-atlas").db("meme_news").collection("news");
return collection.find({});
};
When I press the Run button of their built-in terminal, the correct data is displayed.
On my react's application, I perform the fetch like that:
useEffect(() => {
getData();
}, []);
const getData = async () => {
let getAllData = await user.functions.getAllData();
// all these data are wrong
let tweetId = getAllData
.map((ele) => {
return ele.tweet_id;
})
let tweetIdFirstEle = tweetId[0];
// this return 1537466989966413800
// It should return 1537466989966413825
};
Why is my async/await altering my Mongodb data? I have no idea what is going on here.
I am new in Nextjs, i am trying to integrate [slug.js] page, i want to know that how can we manage/get data in sidebar (similar blogs) ? in other words for blog details i used "get static path" and "props", But now i want to pass "current slug" ( to API) so i can fetch all blogs with this blog category,How can i do this ?
Client-side approach:
Since you pass the post as page-props via getStaticProps, you can either take the slug from there (if it's included in your data model), or extract the slug from the url via next's useRouter hook in case you want to do client-side fetching:
import axios from "axios"; // using axios as an example
import { useRouter } from "next/router";
const Component = () => {
const [similarPosts, setSimilarPosts] = useState([]);
const router = useRouter();
const { slug } = router.query;
const getSimilarPosts = async () => {
if (!router.isReady() || !slug) return [];
const { data } = await axios.get("/api/similar-posts-route/" + slug);
return data;
};
useEffect(() => {
if (similarPosts.length > 0) return;
(async () => {
const posts = await getSimilarPosts(); // assuming API returns an array of posts as data.
setSimilarPosts(posts);
})();
}, []);
return <div>Similar posts: {JSON.stringify(similarPosts)}</div>;
};
[...]
Server-Side approach (preferred):
I believe it would be a better approach to directly fetch similar posts inside getStaticProps to reduce API calls and for a better UX.
Inside getStaticProps you can take the slug from context.params and fetch all similar posts directly from your database/CMS, and pass them directly as props to your page component:
export async function getStaticProps({ params }) {
const { slug } = params;
// fetch similar posts directly from the database using the slug (don't call the API, it's not up yet during build phase)
const similarPosts = await executeDatabaseQueryForSimilarPosts(slug);
// [...] fetch the rest of the page props
return {
props: {
similarPosts,
// [...] return the rest of page props
},
revalidate: 60 * 30 // re-fetch the data at most every 30 minutes, so the posts stay up to date
};
}
// directly take all similar posts from props
const Component = ({similarPosts}) => {
return <div>Similar posts: {JSON.stringify(similarPosts)}</div>;
};
I am using Next.js's Static HTML Export for my site which have 10 million static pages but I am running into ram issues when building the app.
Is it even possible to export it in parts like 100k pages on first build then 100k on second build and so on?
I do not want to use Incremental Static Regeneration or getServerSideProps to cut costs.
This site is using MongoDB only have two pages home page and posts page:
index.js
[postPage].js
In home page I used this code:
export async function getStaticProps() {
const { db } = await connectToDatabase();
const postsFeed = await db
.collection("myCollection")
.aggregate([{ $sample: { size: 100 } }])
.toArray();
return {
props: {
postsFeed: JSON.parse(JSON.stringify(postsFeed)),
},
};
}
In posts page I used this code:
export async function getStaticPaths() {
const { db } = await connectToDatabase();
const posts = await db
.collection("myCollection")
.find({})
.toArray();
const paths = posts.map((data) => {
return {
params: {
postPage: data.slug.toString(),
}
}
})
return {
paths,
fallback: 'blocking'
}
}
export async function getStaticProps(context) {
const postSlug = context.params.postPage;
const { db } = await connectToDatabase();
const posts = await db
.collection("myCollection")
.find({ slug: { $eq: postsSlug } })
.toArray();
const postsFeed = await db
.collection("myCollection")
.aggregate([{ $sample: { size: 100 } }])
.toArray();
return {
props: {
posts: JSON.parse(JSON.stringify(posts)),
postsFeed: JSON.parse(JSON.stringify(postsFeed)),
},
};
}
Doesn't seem to be a built-in option to process batches of static pages https://github.com/vercel/next.js/discussions/14929
I can only think of dividing the work using a bash script where you set an env variable and use it in the code where you're fetching the data to generate the paths, then run the build command as many times as parts you need to split the data, in each iteration move the generated files to another directory that will be your consolidated output.
COUNTER=1
PARTS=100 # change it to control number of parts
while [ $COUNTER -lt $PARTS ]; do
let COUNTER=COUNTER+1
CURRENT=$COUNTER PARTS=$PARTS next build
# move generated files to another directory
done
in your get getStaticPaths
export async function getStaticPaths() {
const currentPercentage = process.env.CURRENT/process.env.PARTS
// logic to fetch the corresponding current percentage of the data
// 1% when there are 100 parts, 0.5% when 200 parts, etc.
}
Be aware that if the data changes very often, you'll see incorrect results, like repeated pages or skipped ones, since each pagination will occur at different moments when running the script. I believe you could create an auxiliary node (or another language) script to better handle that quantity of records, maybe in a streamlined way, and generate JSON files for each chunk of data to use them in getStaticPaths instead of fetching them directly from the DB.
Today I came across one issue in Next.js api. I wanted to fetch data from my internal API in getStaticProps, but as documentation says you should not fetch your local API in getStaticProps and instead you should import the function. But what if I wanted to send a pageIndex to my local API and fetch the specific page. I cant do req.body.page here, so how do I get the data ?
my API looks like this
export const getData = async (page) => {
const data = await fetch(
`https://rickandmortyapi.com/api/character?page=${page}`
);
const response = await data.json();
return response;
};
export default async function getCharactersAPI(req, res) {
//here I would like to get the data that of the page (something like req.body.page)
//and do something like const data = await getData(req.body.page)
const data = await getData();
res.status(200).json(data);
}
and my fetch code looks like this
export const getStaticProps = async pageContext => {
//here I would like to put the page I want to fetch in the getCharactersAPI()
//and do something like const query = await getCharactersAPI(3), meaning page number 3
const query = await getCharactersAPI();
const response = await query.json();
return {
props: { response },
};
};
The pageContext contains the URL parameters, which presumably has the page number that you want to fetch?
So extract the info from the pageContent then call your query.
I am currently struggling displaying the contents of my database in my front end components. I am using the MERN stack to create a grocery list app and so far, I have a form with an onsubmit event in my front end that is sending data to MongoDB and saving it. I see what I am submitting in my database without a problem.
To display the data I have created a backend route where I find the list in my database
backend route image
then I make an api to that backend route via a gentlest action I create
getlist api action
I try calling his action in my front end component but all I see is an empty array in my console not a populated list of any sort. In the past I make the api call then set the state of whatever I need to set and map through that to display it . Let me know if you guys have any ideas
frontend component to show the list
Usually the way you communicate with your backend api is by performing a request via axios or fetch.
If you use axios then create a function and perform a post request to your backend route inside it. Then call that function inside useEffect.
If you are using redux then simply call the action inside useEffect.
Edit:
Let's assume that your server is listening to port 8000 for this example
`
const [groceryList, setGroceryList] = useState([]);
async function fetchData() {
const result = await axios.get(`/http://localhost:8000/getList`);
setGroceryList(result);
}
useEffect(() => {
fetchData()
},[])
`
All You Need To Do:
import {useEffect,useState } from "react";
//use it inside your function component in JS file and make sure your //server 3001 is runing by using express
function abcd(){
const [object, setObj] = useState([
{
title: "",
description: "",
url: "",
},
]);
useEffect(() => {
fetch("http://localhost:3001/pages")
.then((res) => res.json())
.then((jsonRes) => setObj(jsonRes));
}, []);
//Render
return(
<div
style={{
display: "flex",
flexDirection: "column",
width: "20%",
margin: "auto auto",
}}
>
{object
.sort((a, b) => {
var titleA = a.title;
var titleB = b.title;
if (titleA < titleB) {
return -1;
}
if (titleA > titleB) {
return 1;
}
return 0;
})
.map((Ob) => {
return (
<img
key={Ob.title}
src={Ob.url}
alt={Ob.description}
name={Ob.title}
onClick={handleClick}
></img>
);
})}
</div>)
}
//Use the below code in your BackEnd in order to fetch all the data from the //MongoBackEnd(Express)
//Use your MongoDB server address and mongoSchema and store it in a //Variable("Handle")
const Handle = require("./views/mongoschema");
app.get("/pages", (req, res) => {
Handle.find({})
.then((items) => res.json(items))
.catch((err) => console.log(err));
});
By doing the above things you can fetch all the MongoDB data in the frontEnd and add designs or Templates to it.