Why would a query param be undefined in NextJS? - javascript

I'm calling a page withRouter(Page) and expect the variable for the page (the page is called [category].js) to be present on initial page load. Query itself is there, the key is there, but the value is 'undefined.' There seem to be a few calls to getInitialProps on the server side with 2/3 being undefined.
The react component has a constructor, etc. it's not a functional component.
This is my current getInitialProps:
Category.getInitialProps = async ({ req, query }) => {
let authUser = req && req.session && req.session.authUser
let categoryData = {}
let categoryItemData = {}
let category = query.category
if(category){
let res = await fetch(url1,
{
method: 'POST',
credentials: 'include',
})
categoryData = await res.json();
let categoryItemsRes = await fetch(url2,
{
method: 'POST',
credentials: 'include',
})
categoryItemData = await categoryItemsRes.json();
}
return { query, authUser, categoryData, categoryItemData }
}

This might be redundant at this point, but I ran into this as well and found the docs explain this here
During prerendering, the router's query object will be empty since we do not have query information to provide during this phase. After hydration, Next.js will trigger an update to your application to provide the route parameters in the query object.
You might try this instead:
export async function getServerSideProps(ctx) {
const { id } = ctx.query;
return {
props: {
id,
},
};
}
This way it gets the query params when rendering server side, so they're instantly available.

For others who use express custom server, to fix the undefined params, we have to set the dynamic route at server.js as follow:
# server.js
...
app.prepare().then(() => {
const server = express();
....
server.get('/product/:category', (req, res) => {
const { category } = req.params;
return app.render(req, res, `/product/${category}`, req.query)
})
...
}
And then, as Valentijn answers, we can get the category params.
# pages/product/[category].js
....
export async function getServerSideProps(ctx) {
const {category} = ctx.params;
return {
props: {
category
},
};
};
...
The key is dynamic path /product/${category}. Don't use /product/:category

Related

How to pass additional parameter with dynamic routes in Reactjs

I am working in Reactjs and using nextjs,My [slug.js] is working fine with following url
<Link href={`/${post.slug}`}><a>
But i want to send/pass "hidden"(additional parameter) with this,whenever i try to do then i am getting 404 error,I want this because in some page i want to use different api in "serversideprops",Right now here is my code
export const getServerSideProps = async ({ params }) => {
console.log(params); // right now i am getting "slug" as parameter
if(params.anotherparamter)
{
//futher code
}
elseif(params.slug){
const { data: data2 } = await Axios.get(`https://xxxxxxxxxxxxxxxxxxxxxxxxx/${params.slug}`);
}
const blogs = data2;
return {
props: {
blogs: blogs
},
};
};
You can use the as prop to hide the query string.
Your link would look something like this
<Link href={`/${post.slug}?myparam="mysecret"`} as={`/${post.slug}`}></Link> //The link will not show the query param when redirected
You will then be able to access the myparam query in your serverSideProps like so.
export const getServerSideProps = async ({ params, query }) => {
...
const { myparam } = query
console.log(myparam) // will return mysecret as a string
You can read more from the docs

React - axios fetching empty array [duplicate]

I am currently working on social media mern stack react app. I am using node js and express as my backend services , also using mongoose to store my data and axios and redux thunk which connect the backend to the front end. Till now I had no issue recieving and sending data to the server. Right now I am trying to create search post get request ,base on a keyword the user entered. The issue with it, that when I am sending the keyword to the server instead of recieving the string it gets undefined value, like redux thunk not sending anything. I will be very thankful if someone could help me with that. I am watching the code over and over again and can't find out the reason for that.
My post controller class(I copied only the relevant function):
import express from "express";
const app = express();
import Post from "../model/PostModel.js";
import ErrorHandlng from "../utilities/ErrorHandling.js";
import bodyParser from "body-parser";
import catchAsync from "../utilities/CatchAsync.js";
import User from "../model/UserModel.js";
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
export const getPosts = catchAsync(async (req, res, next) => {
const data = req.body.keyword;
const page = parseInt(req.query.page || "0");
const PAGE_SIZE = 20;
const query = new RegExp(data, "i");
const total = await Post.countDocuments({});
const posts = await Post.find({ $or: [{ title: query }, { content: query }] })
.limit(PAGE_SIZE)
.skip(PAGE_SIZE * page);
if (!posts) {
return next(new ErrorHandlng("No posts were found", 400));
}
res.status(200).json({
status: "success",
data: {
totalPages: Math.ceil(total / PAGE_SIZE),
posts,
},
});
});
My api class(front end,copied only the calling for that specific get request):
import axios from "axios";
const baseURL = "http://localhost:8000";
axios.defaults.withCredentials = true;
const API = axios.create({
baseURL,
credentials: "include",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
export const getPostsByKeyword = (keyword, page) =>
API.get(`/post/getPostsByKey?page=${page}`, keyword);
Post slice class:
export const fetchPostsByKeyWord = createAsyncThunk(
"post/getKeyword",
async ({ keyword, page }, { fulfillWithValue, rejectWithValue }) => {
try {
const response = await api.getPostsByKeyword(keyword, page);
if (response.statusCode === "400") {
throw new Error("There are no available posts");
}
const fetchData = await response.data.data.posts;
const totalPages = await response.data.data.totalPages;
return fulfillWithValue({ fetchData, totalPages });
} catch (err) {
console.log(err.response.message);
}
}
);
const initialState = { status: "undefined" };
const PostSlice = createSlice({
name: "post",
initialState,
reducers: {},
extraReducers: {},
});
export const postActions = PostSlice.actions;
export default PostSlice;
Calling the backend:
dispatch(fetchPostsByKeyWord({ keyword, page }))
.unwrap()
.then((originalPromiseResults) => {
console.log("thte " + " " + originalPromiseResults.totalPages);
console.log("The data is" + originalPromiseResults.fetchData);
setTotalPages(originalPromiseResults.totalPages);
})
.catch((err) => {
console.log(err.message);
});
As you can see I have not copied the whole code, I copied only the parts that are relevants for the question.
Browsers cannot currently send GET requests with a request body. XMLHttpRequest (which Axios uses) will ignore it and fetch() will trigger an error.
See also HTTP GET with request body for extra discussion on why trying this might be a bad idea.
You should instead pass everything required in the query string, preferably via the params option so it is correctly encoded...
export const getPostsByKeyword = (keyword, page) =>
API.get("/post/getPostsByKey", { params: { page, keyword } });
and grab the data via req.query server-side.
const { page, keyword } = req.query;
With vanilla JS, you can use URLSearchParams to construct the query string...
const params = new URLSearchParams({ page, keyword });
// XHR
const xhr = new XMLHttpRequest();
xhr.open("GET", `/post/getPostsByKey?${params}`);
// Fetch
fetch(`/post/getPostsByKey?${params}`); // GET is the default method
Your Axios instance creation could also be a lot simpler...
Axios is usually quite good at setting the correct content-type header, you don't have to
Your Express app isn't doing any content-negotiation so you don't need to set the accept header
Unless you're actually using cookies (which it doesn't look like), you don't need credential support
const API = axios.create({ baseURL });

How to fetch Strapi by slug, and how to populate the categories

I'm trying to fetch post for a react blog with strapi backend using the slug.
I created the custom route and custom controller, but the value returned is missing a few attributes like images and category.
When I fetch using post Id, I use query string to populate the object returned, but I don't know how to had qs to the slug API route.
Below is the custom controller, and the custom route
///custom controller
async findOne(ctx) {
const { slug } = ctx.params;
const { query } = ctx;
const entity = await strapi.service('api::article.article').findOne(slug, query);
const sanitizedEntity = await this.sanitizeOutput(entity, query);
return this.transformResponse(sanitizedEntity);
}
///Custom Route
{
method: 'GET',
path: '/articles/slug/:slug',
handler: 'custom-controller.findOne',
config: {
policies: []
},
This is how I fetch from client in useEffect
useEffect(()=>{
const fetchData = async()=>{
// const query = qs.stringify({
// populate: '*',
// }, {
// encodeValuesOnly: true,
// });
const res = await axios.get(`http://localhost:1337/api/articles?filters[slug][$eq]=${slug}`)
console.log(res.data)
updateState(res.data)
}
fetchData()
setLoading(false)
}, [slug])
I've also tried to use the Entity API Service, but I just couldn't get it to work.
How do I populate the object to include these missing attributes?
With Strapi v4 you can do it this way
1. Create a file in src/api/article/_custom.js
Please note I put an underscore because:
Routes files are loaded in alphabetical order. To load custom routes before core routes, make sure to name custom routes appropriately (e.g. 01-custom-routes.js and 02-core-routes.js).
Source: https://docs.strapi.io/developer-docs/latest/development/backend-customization/routes.html#creating-custom-routers
module.exports = {
routes: [
{
method: 'GET',
path: '/articles/:slug',
handler: 'article.findOne',
config: {
auth: false
},
}
]
}
2. Edit the src/api/article/controllers/article.js
'use strict';
/**
* article controller
*/
const { createCoreController } = require('#strapi/strapi').factories;
module.exports = createCoreController('api::article.article', ({ strapi }) => ({
// Query by slug
async findOne(ctx) {
// thanks to the custom route we have now a slug variable
// instead of the default id
const { slug } = ctx.params;
const entity = await strapi.db.query('api::article.article').findOne({
where: { slug }
});
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
},
}));
Now you can call your api this way:
http://localhost:1337/api/articles/my-beautiful-article-about-orange
Reference: https://www.youtube.com/watch?v=OVV0CfgX6Qk
Note: In the video, custom.js is loaded before post.js ^^

using NextAuth hooks useSession() inside getServerSideProps()

I'm trying to get some data from my server depending on whose currently logged in. I'm using Next-Auth and normally I can just call:
const { data: session } = useSession();
At the top of the functional component, but you cannot do this in getServerSideProps().
I need to make a get request like this:
export async function getServerSideProps() {
const res = await fetch(
`http://localhost:5000/api/users/${session.id}/following`
);
const isFollowing = res.json();
return {
props: { props: isFollowing },
};
}
that has the current users session ID dynamically put in.
How do I access my session ID inside getServerSideProps?
Since useSession is react-hook - it can be used only inside Component. For server-side usage there another method from Next-Auth package - getSession.
https://next-auth.js.org/v3/getting-started/client#getsession
Server-Side Example
import { getSession } from "next-auth/client"
export default async (req, res) => {
const session = await getSession({ req })
/* ... */
res.end()
}
Note: When calling getSession() server side, you need to pass {req} or context object.
you should to re-assign the headers from the getServerSideProps request to inner fetch, because that fetch has no headers, cookies or tokens
export async function getServerSideProps(ctx) {
const headers=ctx.req.headers //where cookies, jwt or anything
const res = await fetch(
`http://localhost:5000/api/users/${session.id}/following`,
{headers}
);
const isFollowing = res.json();
return {
props: { props: isFollowing },
};
}

Why is my axios post returning undefined in my functional component?

I'm new to Next Js and functional comoponents. I'm trying to retrieve data from /api/retrieve2
//this is retrieve page
export default function Retrieve() {
const onSubmit = async data => {
const { user } = await axios.post("/api/retrieve2", data);
console.log(user) // user here is undefined
};
return (...);
}
//this is retrieve2, inside the API folder
export default async (req, res) => {
try {
const { data } = await axios.post(myBackendUrl, req.body);
console.log(data) //this is printing the right data - { email: 'casas#gmail.com', code: '123123' }
res.json(data);
} catch (e) {
res.json({ err: e.message || e });
}
};
What am I missing, is this something about Next? About functional components?
You should read about ES6 destructuring
You try to destructure user but the axios respons witch is a object doesnt contain the key user
For data it works because there is a data property in the response
Here are all properties that you can destructure:
{ data, status, statusText, headers, config, request }
You need to get the full URL to make http request to using getInitialProps, here Home is the name of your component
const Home = ({ENDPOINT}) => {
const onSubmit = async data => {
const { data } = await axios.post(`${ENDPOINT}/api/retrieve2`, data);
// consider changing `user` here to `data` since Axios stores response in data object
console.log(data) // should be defined
};
return (...);
}
Home.getInitialProps = ctx => {
const ENDPOINT = getEndpoint(ctx.req);
return { ENDPOINT };
};
// You should store this somewhere you can reuse it
export function getEndpoint(req) {
return !!req
? `${req.headers['x-forwarded-proto']}://${req.headers['x-forwarded-host']}`
: window.location.origin;
}

Categories