So I have getStaticProps and need to do some data fetching based on a variable here is an example
export async function getStaticProps() {
const res = await fetch(localWordpressUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `
query AllPostsQuery {
posts(where: {categoryName: "blog"}) {
nodes {
slug
content
title
}
}
}
`,
})
});
const json = await res.json();
return {
props: {
posts: json.data.posts,
}
};
}
Where categoryName: "blog" needs to be a variable instead of hard coded. I know you can get the slug, but what I need is before the slug. i.e. site.com/blog/slug. Any suggestions on this?
You're actually really close. What you need is to grab the context out of your getStaticProps function. Add this to your getStaticProps function, and look at the console log to see what's happening.
export async function getStaticProps(context) {
console.log(JSON.stringify(context, null, 3));
const { asPath, req, res, pathname, query } = context;
if (req) {
let localWordpressURL = req.headers.host;
console.log(localWordpressURL);
}
}
Related
I am trying to perform a delete function on my nextjs app using mongoose, I was able a successfully achieve POST, GET method but still find it difficult to achieve the delete operation.
My POST method inside in API folder:
export default async function addUser(req, res) {
const data = req.body
await connectDB()
const myDocument = await userModel.create(data)
res.json({ myDocument })
}
Here is how I called it from my frontend:
async function Login(e) {
e.preventDefault()
const userObject = {
user_name: userName,
password: password
}
const response = await fetch('/api/add', {
method: 'POST',
body: JSON.stringify(userObject),
headers: {
'Content-Type': 'application/json'
}
})
const data = await response.json()
console.log(data)
}
I was able to read it using this method and parse the data through props and map through:
export const getServerSideProps = async () => {
await connectDB()
const myDocument = await userModel.find()
return {
props: {
myDocument: JSON.parse(JSON.stringify(myDocument))
}
}
}
How do perform the DELETE method?
I tried this:
export default async function Remove(req, res) {
await connectDB()
await userModel.deleteOne({_id: req.params.id}, function (err) {
if (err) {
console.log(err)
}
res.send("Deleted")
})
}
which is normally what will work using my node and express, But is not working here.
Here is the frontend function I tried:
function Delete(_id) {
fetch(`/api/remove/${_id}`)
.then(() => {
window.location.reload()
})
}
But it's not working.
So after a long study, I was able to come up with a solution.
I created a dynamic route in my "API" folder called "[id].js" and wrote the following code:
export default async (req, res) => {
const {query: {id}} = req
await connectDB()
const deletedUser = await userModel.findByIdAndDelete(id)
if (!deletedUser) return res.status(404).json({msg: "does not exist"})
return res.status(200).json()}
I edited my front-end to be:
async function Delete(_id) {
await fetch(`/api/${_id}`, {
method: 'DELETE'
}).then(() => {
//Do something here
})
}
I'm trying to do a delete request. I can fetch the API route through pages/api/people/[something].js.
And this is the response I got from the browser's console.
DELETE - http://localhost:3000/api/people/6348053cad300ba679e8449c -
500 (Internal Server Error)
6348053cad300ba679e8449c is from the GET request at the start of the app.
In the Next.js docs, for example, the API route pages/api/post/[pid].js has the following code:
export default function handler(req, res) {
const { pid } = req.query
res.end(Post: ${pid})
}
Now, a request to /api/post/abc will respond with the text: Post: abc.
But from my API route pages/api/people/[something].js, something is undefined.
const { something } = req.query
UPDATED POST:
React component
export default function DatabaseTableContent(props) {
const id = props.item._id; // FROM A GET REQUEST
const hide = useWindowSize(639);
const [deletePeople] = useDeletePeopleMutation();
async function deleteHandler() {
await deletePeople(id);
}
return <Somecodes />;
}
apiSlice.js
export const apiSlice = createApi({
// reducerPath: "api",
baseQuery: fetchBaseQuery({ baseUrl: url }),
tagTypes: ["People"],
endpoints: (builder) => ({
getPeople: builder.query({
query: (people_id) => `/api/people/${people_id}`,
providesTags: ["People"],
}),
deletePeople: builder.mutation({
query: (studentInfo) => ({
url: `api/people/people-data/student-info/${studentInfo}`,
method: "DELETE",
headers: {
accept: "application/json",
},
}),
invalidatesTags: ["People"],
}),
}),
});
export const {
useGetPeopleQuery,
useDeletePeopleMutation,
} = apiSlice;
pages/api/people/people-data/student-info/[studentInfo].js
import { ObjectId, MongoClient } from "mongodb";
async function handler(res, req) {
const { studentInfo } = req.query; // the code stops here because "studentInfo" is undefined
const client = await MongoClient.connect(process.env.MONGODB_URI.toString());
const db = client.db("people-info");
if (req.method === "DELETE") {
try {
const deleteData = await db
.collection("student_info")
.deleteOne({ _id: ObjectId(studentInfo) });
const result = await res.json(deleteData);
client.close();
} catch (error) {
return res.status(500).json({ message: error });
}
}
}
export default handler;
The order of params passed to your handler functions needs to be reversed.
For NextJS API routes the req is the first param passed to the handler and the res param is second.
Example handler function from NextJS documentation:
export default function handler(req, res) {
res.status(200).json({ name: 'John Doe' })
}
I have cards with product information in my database, I display them successfully on the user's page. Now I want to add a more details button on each card to go to a new page from it (/pages/card/[id]). But I don't really understand how I can pull out the card value by clicking through my API.
const res = await fetch('/api/cards/' + id, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ id: id })
})
if (res.ok) {
const result = await (await res).json()
if (result.redirectUrl) {
router.push(result.redirectUrl as string)
}
}
}
API
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { id } = req.query
if (req.method === 'GET') {
if (typeof id === 'string') {
const moreDetail= await db.sales.findUnique({
where: {
id: id },
})
res.send({ redirectUrl: '/card'+[id] })
}
}
My card in schema
id String #id #default(cuid())
title String
description String
active Boolean #default(true)
My suggestion would be to introduce another API endpoint that returns an array of all of the available cards, or at least an array of all of the available card ids. After that, create a new page matching your URL format /pages/card/[id].tsx and inside that file, create your page like normal, but also export 2 functions:
getStaticPaths
getStaticProps
These let Next know what paths are available and how to load data for them during the build process.
export async function getStaticPaths() {
const cardIds = await fetch('/api/cards', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
});
return {
paths: cardIds.map((id) => (
{
params: { id }
},
)),
fallback: false, // setting to false will throw a 404 if none match
};
}
This lets Next know all of the available dynamic routes to generate pages for.
export async function getStaticProps({ params: { id } }) {
const card = await fetch(`/api/cards/${id}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
});
return {
props: {
card,
},
}
}
This actually loads the data from your API given a card id and passes it into your component to display more details for.
Hopefully that gives you a good jumping off point.
I have a next.js App which has a working axios call, which I am trying to refactor. I have it mostly working, but I can't get my new function to receive arguments.
This problem has two components to it, my next.js page, and the external custom module where I am writing my functions to use axios to call the YouTube API to retrieve info.
My next.js getStaticProps call looks like this. I know this is working. Note the function where I am trying to pass in the video ID. (The 'const = video' line)
export async function getStaticProps(context: any) {
// It's important to default the slug so that it doesn't return "undefined"
const { slug = "" } = context.params;
const film = await client.fetch(query, { slug });
const video = await youtube.grabVideoInfo(film.VideoID);
return {
props: {
film,
video,
},
revalidate: 10,
};
}
I have tried writing the axios call in two ways, trying to pass in the video ID as an argument. Neither of which work, and fail to call from the API, stating an invalid video ID, which means it isn't being passed in.
The first way:
const grabVideoInfo = async (videoId) => {
const videoGrab = axios.create({
baseURL: "https://www.googleapis.com/youtube/v3/videos?",
params: {
headers: { "Access-Control-Allow-Origin": "*" },
part: "snippet",
id: videoId,
key: KEY,
},
});
const query = await videoGrab.get().then(
(response) => {
return response.data.items[0];
},
(error) => {
return error.toJSON();
}
);
return query;
};
The second way:
const grabVideoInfo = async (videoId) => {
const videoGrab = axios.create({
baseURL: "https://www.googleapis.com/youtube/v3/videos?",
params: {
headers: { "Access-Control-Allow-Origin": "*" },
part: "snippet",
key: KEY,
},
});
const query = await videoGrab.get({ params: { id: videoId } }).then(
(response) => {
return response.data.items[0];
},
(error) => {
return error.toJSON();
}
);
return query;
};
And this is the fully working version that I am trying to rewrite, which is live on the app currently. This demonstrates that the getStaticProps client call is working.
export async function getStaticProps(context: any) {
// It's important to default the slug so that it doesn't return "undefined"
const { slug = "" } = context.params;
const film = await client.fetch(query, { slug });
const KEY = process.env.YOUTUBE_API_KEY;
const conn = axios.create({
baseURL: "https://www.googleapis.com/youtube/v3/",
params: {
headers: { "Access-Control-Allow-Origin": "*" },
part: "snippet",
id: film.videoID,
key: KEY,
},
});
const video = await (await conn.get("videos?")).data.items[0];
return {
props: {
film,
video,
},
revalidate: 10,
};
}
Any help is greatly appreciated. I'm really scratching my head with this one.
Ok so your refactor is accessing film.VideoId where the original uses film.videoId.
I'm trying to access a function that I'm later going to use for changing some database values. However whenever I try to build the project I get:
Error: Route.post() requires callback functions but got a [object Promise]
My initial function is:
import fetch from '../../../../core/fetch';
import history from '../../../../core/history';
export const BOXOFFICE_CHECKING_IN = 'BOXOFFICE_CHECKING_IN';
export const BOXOFFICE_CHECKED_IN = 'BOXOFFICE_CHECKED_IN';
export const BOXOFFICE_CHECKED_IN_ERROR = 'BOXOFFICE_CHECKED_IN_ERROR';
export default function checkIn() {
return async (dispatch, getState) => {
try {
dispatch({ type: BOXOFFICE_CHECKING_IN });
const state = getState();
const {
order: {
id: orderId,
},
} = state;
const response = await fetch(
`/api/event/orders/${orderId}/checkIn`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
order: orderId,
checkedIn: true,
}),
}
);
if (response.status === 200) {
dispatch({ type: BOOKING_CHECKED_IN });
} else {
const errorResponse = await response.json();
if (errorResponse.code === 'card_error') {
dispatch({ type: BOXOFFICE_CHECKED_IN_ERROR });
}
}
} catch (err) {
throw err;
}
};
}
Which then feeds to the api:
import checkIn from '../handlers/api/orders/checkInCustomer';
...
export default (resources) => {
const router = new Router();
...
router.post('/orders/:orderId/checkIn', checkIn(resources));
Which then reaches the final function I wish to use:
export default async function checkIn(req, res) {
console.log('this is working fully');
return true;
}
Any help is appreciated.
The problem is, you want to be passing the function checkIn, but when you call it using checkIn(resources) you're actually passing the return value (A promise that resolves to true).
You should be using:
router.post('/orders/:orderId/checkIn', checkIn);
Now, I'm assuming you want to do this because you want to pass resources into the router.post function, correct? What happens to the request and response objects?
Where does resources go?
v
export default async function checkIn(req, res) {
console.log('this is working fully');
return true;
}
You have a few ways of accomplishing what you're looking for.
Create a resources file, and import it. This is the ideal solution:
const db = mysql.connect(...);
const lang = lang.init();
console.log('This file is only called once!');
export default {
db,
lang,
};
And then in your code (/routes/checkIn.js):
import { db } from '../resources';
export default async function checkIn(req, res) {
//Access db here
//db.query...
}
Wrap your code in an intermediate function:
router.post('/orders/:orderId/checkIn', (req, res) => checkIn(req, res, resources));
Bind() resources to your checkIn function:
const db = mysql.connect(...);
const lang = lang.init();
const resources = {db, lang};
router.post('/orders/:orderId/checkIn', checkIn.bind(resources));
And then in your code (/routes/checkIn.js):
export default async function checkIn(req, res) {
//Access this.db here
//this.db.query...
}