CRUD with nextjs using mongoose - javascript

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
})
}

Related

req.query is undefined in Next.js API route

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' })
}

How can I persist auth state in a nodejs app

So, I am learning NodeJs by creating this backend that fetches some data from a third-party API, the API requires auth. I couldn't figure out how to avoid sending an auth request to the third-party API whenever I wanted to fetch data from it. is there any way I could store the auth state in the app?
const axios = require("axios");
const AUTH_URL = process.env.AUTH_URL;
const REPORT_BASE_URL = process.env.REPORT_BASE_URL;
const X_API_KEY = process.env.X_API_KEY;
const getCompanies = async (req, res) => {
let idToken;
// auth
const authPayload = JSON.stringify({
// ...
});
const config = {
method: "post",
// ...
};
try {
const { data } = await axios(config);
idToken = data.idToken; // set idToken necessary for fetching companies
} catch (error) {
console.log(error);
}
// get company by full text query
const { full_text_query } = req.query;
if (!full_text_query)
return res.send("No full_text_query parameter provided");
try {
const { data } = await axios.get(
`${REPORT_BASE_URL}/companies?full_text_query=${full_text_query}`,
{
headers: {
"x-api-key": X_API_KEY,
Accept: "application/json",
authorization: idToken,
},
}
);
res.status(200).json(data);
} catch (error) {
console.log(error);
}
};
module.exports = {
getCompanies,
};
You can break out a function like fetchIdToken and store a Promise that resolves with the idToken in memory.
let idTokenPromise;
async function fetchIdToken () {
if (idTokenPromise) return idTokenPromise;
return idTokenPromise = new Promise(async (resolve) => {
...
resolve(data.idToken);
})
}
You can then use await fetchIdToken() at the start of getCompanies.
You can also just store the idToken in memory. This is slightly simpler, but does mean that you can have a race-condition when multiple getCompanies requests happen at the same time:
let idToken;
async function fetchIdToken () {
if (idToken) return idToken;
...
idToken = data.idToken;
return idToken;
}

Getting undefined in API call via Fetch - Express Nuxt

I am extremely stuck on a task using Express, API, Fetch.
I am using Nuxt + Shopify API endpoints to grab data such as orders like below
This is my express API Endpoint.
The results should return an array of objects ( orders )
const bodyParser = require('body-parser')
const app = require('express')()
const axios = require('axios')
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.post('/getJSON', async (req, res) => {
const { customerID } = req.body
const id = Buffer.from(customerID, 'base64')
.toString('binary')
.split('gid://shopify/Customer/')
.pop()
console.log('id is', id)
const endpoint = `https://test.myshopify.com/admin/api/2020-07/customers/${id}/orders.json`
try {
const response = await axios.get(endpoint, {
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': '*****************'
}
})
res.status(200).json(response.data.orders)
} catch (error) {
res.status(500).send(error)
}
})
module.exports = app
Now, in my Nuxt store.js, I am using fetch to make a post requests to that endpoint above.
async function apiPost(endpoint, { data }) {
await fetch(`/api${endpoint}/getJSON`, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
}).then(async (res) => {
const contentType = res.headers.get('content-type')
if (contentType.startsWith('text/html')) {
return res.text()
}
if (contentType.startsWith('application/json')) {
await res.json()
}
})
}
When I console.log res.json(), it is a promise
Since it is a promise, I wanted to see what the results were anyways.
res.json().then((resp) => {
console.log('resp is', resp)
})
Turns out the rest is an array of objects.
However, I do not know how to return this object correctly, as the "ordersResponse" below is always undefined !
Below is a function calling the apiPost passing in the 'endpoint'. However, the orderResponse is undefined. I have tried solutions, but all of them end up as orderResponse being undefined.
async fetchOrders({ state, dispatch, commit }, payload) {
try {
const ordersResponse = await apiPost('/customer-orders', {
data: { customerID: state.customer.id }
})
console.log('ordersResponse', ordersResponse) // **undefined**
} catch (error) {
console.error(error)
throw error
}
},
Any help is appreciated
It looks like the apiPost function needs to make a return from within the 'application/json' if block.
if (contentType.startsWith('application/json')) {
return res.json()
}
You should then receive data back when calling it for the ordersResponse variable.

Handling query in React and Express

Somewhere in my React application I used REST API to send request to the server. In my URL I want to use query (in the postIconsTransition method), but when I send a request to the server, server tells me could not found this URL (I build this error in my server). If I use this URL without any query the request in the postIconsTransition method works fine. postId and authContext.userId work fine, can anyone tell me what's wrong with my code?
In my component where I send request:
const likeHandler = async () => {
setLike(prevState => !prevState);
if (!like) {
try {
await postIconsTransition(props.postId, "inc");
} catch (error) {}
} else {
try {
await postIconsTransition(props.postId, "dec");
} catch (error) {}
}
};
In useHttp.js component:
const postIconsTransition = async (postId, addtionAddress) => {
return await transitionData(
`http://localhost:5000/post/${postId}/${authContext.userId}?t=${addtionAddress}`,
"POST",
null,
{ Authorization: `Bearer ${authContext.token}` }
);
};
transitionData method:
const transitionData = useCallback(
async (url, method = "GET", body = null, headers = {}) => {
setIsLoading(true);
const abortController = new AbortController();
activeHttpRequest.current.push(abortController);
try {
const response = await fetch(url, {
method,
body,
headers,
signal: abortController.signal
});
const responseData = await response.json();
activeHttpRequest.current = activeHttpRequest.current.filter(
reqCtrl => reqCtrl !== abortController
);
if (!response.ok) {
throw new Error(responseData.message);
}
setIsLoading(false);
return responseData;
} catch (error) {
modalContext.err(error);
setIsLoading(false);
throw error;
}
},
[modalContext.err]
);
In Express:
router.post(
"/:postId/:userId?t=inc",
tokenChecker,
postController.updateLikesComments
);
router.post(
"/:postId/:userId?t=dec",
tokenChecker,
postController.updateLikesComments
);
All of them work fine but when I use query in my URL, it's not working any more.
You don't specify query parameters in express routes like that. Just send them. Express can read it.
router.post(
"/:postId/:userId",
tokenChecker,
postController.updateLikesComments
);
// Notice that you don't need the other one.
and in your controller check the parameter
// controller's code
const t = req.query.t;
if (t === 'inc') {
// do what you want here
}
if (t === 'dec') {
// do what you want here
}

Trying to build project throws: requires callback functions but got a [object Promise]?

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...
}

Categories

Resources