Nodejs re parse JSON - json

I am unsure why with mongodb and Nextjs I am having to re do JSON.parse on data that I have fetching from MongoDB.
function PodcastUpload({ data }) {
const [value, setValue] = useState('');
var eD = JSON.parse(data);
eD = eD[0]
the data is from
export async function getServerSideProps(ctx) {
const token = await getSession(ctx)
const data = await getLatestEpisodeData(token,ctx.params.podcastName)
return { props: { data } }
}
which gets the data from
export async function getLatestEpisodeData(token, showname) {
if (token) {
// Signed in
const uuid = token.uuid;
try {
const client = await clientPromise;
const db = client.db("DRN1");
const shows = await db
.collection("episode")
//.findOne({show_b: showname})
//.toArray()
.find({'show_b': showname})
.sort({_id:-1})
.limit(1)
.toArray();
return(JSON.stringify(shows));
} catch (e) {
console.error(e);
}
}
}
I have tried to remove the JSON.stringify however that brings up a SerializableError: Error serializing .datareturned fromgetServerSideProps in "/staff/podcasts/[podcastName]/upload".

Have you tried logging data in your getServerSideProps ?
I usually get this error when one of my props is undefined

Related

Cannot map results from API

I'm trying to dynamically generate routes in my next.js application. I have an api called getUsers that returns something like this:
{"users":[{"_id":"639a87ae8a128118cecae85b","username":"STCollier","image":"https://storage.googleapis.com/replit/images/1641322468533_db666b7453a6efdb886f0625aa9ea987.jpeg","admin":false,"likedPosts":["639e34c5991ecaea52ace9e4","639e34c7991ecaea52ace9e7","639e34c7991ecaea52ace9ea","639e39a216a642f686a28036","639e39a216a642f686a28037","639e3b3d8cdebd89d9691f97","639e3b3d8cdebd89d9691f98","639e3b3e8cdebd89d9691f9d","639e3b5a8cdebd89d9691fa0","639e3b5c8cdebd89d9691fa3","639e3b5c8cdebd89d9691fa6"],"dislikedPosts":[""]},{"_id":"639a88abc4274fba4e775cbe","username":"IcemasterEric","image":"https://storage.googleapis.com/replit/images/1646785533195_169db2a072ad275cfd18a9c2a9cd78a1.jpeg","admin":false,"likedPosts":[],"dislikedPosts":[]}
So I know the API works succesfully, but when trying to get these api results and generate a page for each username, I get an error stating:
TypeError: users.map is not a function
Here's my code for generating the routes:
//pages/user/[username].js
const Get = async (url) => {
return await fetch(url).then(r => r.json());
}
export async function getStaticPaths() {
const users = Get('/api/getUsers')
return {
paths: users.map(u => {
const username = u.users.username
return {
params: {
username
}
}
}),
fallback: false
}
}
What is wrong with my getStaticPaths() code? I know that the API is working, so why can't I map the results?
And if anyone needs the code for api/getUsers, here is that:
import clientPromise from "../../lib/mongodb";
import nc from "next-connect";
const app = nc()
app.get(async function getUsers(req, res) {
const client = await clientPromise;
const db = client.db("the-quotes-place");
let users = []
try {
const dbUsers = await db
.collection("users")
.find({})
.toArray();
users = dbUsers
return res.json({
users: JSON.parse(JSON.stringify(users)),
success: true
})
} catch(e) {
return res.json({
message: new Error(e).message,
success: false,
});
}
})
export default app
Thanks for any help!!
Modify Get method to return an async value instead of Promise.
As Get is an async method, you need the await in getStaticPaths method.
const Get = async (url) => {
let response = await fetch(url);
return await response.json();
}
export async function getStaticPaths() {
const users = await Get('/api/getUsers');
...
}

Fetch API working in options API but not in compositions API

I have some code that I want to convert from options API to composition API and in composition API I'm getting res.json is not a function error while the same code is working fine in options API.
Here's the code for options API
<script>
export default {
name: 'ProductComponent',
data(){
return {
productData: []
}
}
methods: {
async retrievedData(){
const res = await fetch('../productsInfo.json');
const resData = await res.json()
this.productData = resData
}
}
}
</script>
Here's the code for composition API
<script>
import { ref } from 'vue'
export default {
name: 'ProductComponent',
setup() {
const productData =ref([])
const retrievedData = async() => {
const res = await fetch('../productsInfo.json');
const resData = await res.json()
productData.value = await resData
}
}
</script>
Not using return for productData and retrievedData in setup method because this data is not being exposed.

How do I return an asynchronous DB query result from one module to another using Node.js?

I'm new to Node, and I'm trying to follow a pattern from a Udemy API course. The API is structured to utilize route, controller and service modules for flow. Database queries are to be run as services and they are supposed to be called from controllers.
I need to run a series of DB queries to generate a list (I'm showing only 2 of 6 queries in this example). I am running these using async/await in my function. The queries are working fine. My problem occurs when I try to return the 'batch result' (the result of all the queries) to the controller at the end of the process. I get Promise { <pending> }. I have tried many things, but I cannot end the promise to access the final result from my controller module--I can only access it from my service module.
Here is my code from my controller module (groups.controller.js) where I call my function:
const groupsService = require('../services/groups.service');
exports.propertyList = (req, res, next) => {
const uid = req.body.uid;
const batchResponse = groupsService.batchQuery(uid, res);
console.log(batchResponse);
}
And here is my code from my service module (groups.services.js) where I run the queries:
const mysql = require('mysql2');
const dbAsync = require("../config/db.config");
async function batchQuery(uid, res) {
var Q1;
var Q2;
var uid = uid * -1;
const pool = mysql.createPool(dbAsync.dbAsync);
const promisePool = pool.promise();
try {
Q1 = await promisePool.query('SELECT PropertyID FROM GroupMembership WHERE GroupID = ?', [uid]);
Q2 = await promisePool.query('SELECT SubGroupID FROM GroupMembership WHERE GroupID = ? AND PropertyID = ?', [uid, 0]);
}
catch(error) {
console.log(error);
res.status(401).send('Server error');
return error;
}
finally {
const batchResponse = {
Q1: Q1[0],
Q2: Q2[0]
}
console.log('Q1: '+ Q1[0][0].PropertyID + ', Q2: ' + Q2[0][0].SubGroupID);
res.status(200).send(batchResponse);
return batchResponse;
}
}
module.exports = {batchQuery};
When I send a post via postman, I get the expected query result (below). However, I can only get this to work if I put my res in my service module.
{
"Q1": [
{
"PropertyID": 0
}
],
"Q2": [
{
"SubGroupID": 397
}
]
}
Is there a way to end the promise in this pattern and return the desired batch response? Thank you.
EDIT: Adding the code updates provided by #traynor.
New controller:
const groupsService = require('../services/groups.service');
exports.propertyList = async (req, res, next) => {
const uid = req.body.uid;
let batchResponse;
try {
batchResponse = await groupsService.batchQuery(uid);
console.log(batchResponse);
return res.status(200).send(batchResponse);
} catch(error) {
console.log('Error: ' + error);
return res.status(401).send('Server error');
}
}
New service:
const mysql = require('mysql2');
const dbAsync = require("../config/db.config");
function batchQuery(uid) {
return new Promise((resolve, reject) => {
var Q1;
var Q2;
var uid = uid * -1;
const pool = mysql.createPool(dbAsync.dbAsync);
const promisePool = pool.promise();
try {
Q1 = await promisePool.query('SELECT PropertyID FROM GroupMembership WHERE GroupID = ?', [uid]);
Q2 = await promisePool.query('SELECT SubGroupID FROM GroupMembership WHERE GroupID = ? AND PropertyID = ?', [uid, 0]);
} catch(error) {
console.log(error);
reject(error);
} finally {
const batchResponse = {
Q1: Q1[0],
Q2: Q2[0]
}
console.log('Q1: '+ Q1[0][0].PropertyID + ', Q2: ' + Q2[0][0].SubGroupID);
resolve(batchResponse);
}
})
}
module.exports = {batchQuery};
the service is now returning a promise, and it's also handling response instead of controller.
to return from service, you need to promisify service: return a promise which resolves when you get db data, or on error, and then you also need to await the service, which it's wrapped in try/catch for error handling.
once it's all done, handle response from the controller:
service:
function batchQuery(uid) {
return new Promise(async (resolve, reject) => {
var Q1;
var Q2;
//...
try {
//...
} catch (error) {
console.log(error);
reject(error);
} finally {
const batchResponse = {
Q1: Q1[0],
Q2: Q2[0]
}
console.log('Q1: ' + Q1[0][0].PropertyID + ', Q2: ' + Q2[0][0].SubGroupID);
resolve(batchResponse);
}
});
controller:
exports.propertyList = async (req, res, next) => {
const uid = req.body.uid;
let batchResponse;
try {
batchResponse = await groupsService.batchQuery(uid);
console.log(batchResponse);
res.status(200).send(batchResponse);
} catch(error) {
return res.status(401).send('Server error');
}
}

invalid json response body at http://localhost:3000/api/user/[object%20Object] reason: Unexpected token < in JSON at position 0

I'm using this example to create a simple authentication with Nextjs https://github.com/vvo/next-iron-session/tree/master/examples/next.js
but instead of fetching the user JSON object from Github (as the example does) im trying to do it from my mongodb database where i have some users.
I did this on my login.js file:
import fetchJson from "../../lib/fetchJson";
import withSession from "../../lib/session";
import { withIronSession } from "next-iron-session";
import { connectToDatabase } from "../../util/mongodb";
export default withSession(async (req, res) => {
const { db } = await connectToDatabase();
const { username } = await req.body;
const foundUser = await db.collection("users").findOne({"userName": username});
console.log(foundUser) // <--- this returns the user object on console just fine
const url = `http://localhost:3000/api/user/${foundUser}`;
try {
const { userName, email } = await fetchJson(url);
const user = { isLoggedIn: true, userName, email }
req.session.set("user", user);
await req.session.save();
res.json(user);
} catch (error) {
const { response: fetchResponse } = error;
res.status(fetchResponse?.status || 500).json(error.data);
}
});
And i have this code on my /api/user.js file:
import withSession from "../../lib/session";
import { connectToDatabase } from "../../util/mongodb";
export default withSession(async (req, res) => {
const user = req.session.get("user");
if (user) {
const { db } = await connectToDatabase();
const foundUser = await db.collection("users").findOne({"userName": user.userName, "email": user.email});
console.log("useri pi te user.js " + foundUser)
// in a real world application you might read the user id from the session and then do a database request
// to get more information on the user if needed
res.json({
isLoggedIn: true,
...user,
});
} else {
res.json({
isLoggedIn: false,
});
}
});
But i get "invalid json response body at http://localhost:3000/api/user/[object%20Object] reason: Unexpected token < in JSON at position 0" error even though i get the user object printed in the console just fine.
Any help would be appreciated!

Cannot read property 'getAllProducts' of undefined

The problem is in db.js because it's trying to load something from db which does not exist.
In my index.js page:
const dataB = require("./db").getAllProducts;
app.get('/scrapper', async (req, res) => {
const Myobjects = await dataB.getAllProducts();
res.send(Myobjects)
})
And in my db.js page:
async function getAllProducts() {
const connection = await getConnection();
const pageRepo = connection.getRepository(Crawlers);
const pages = await pageRepo.find();
connection.close();
return pages;
}
async function InsertScrappedData(texte, image, price){
const connection = await getConnection();
const page = new Crawler();
page.texte = texte;
page.image = image;
page.price = price;
const crawlertrepo=connection.getRepository(Crawlers);
const res=await crawlertrepo.save(Crawlers);
Console.log('saved',res);
const Allpages = await crawlertrepo.find();
connection.close();
return Allpages;
}
Exporting my functions
module.exports = [
getAllProducts,
InsertScrappedData
]
module.exports is an object type, not an array, so you need to use curly brackets when assigning it.
module.exports = {
getAllProducts,
InsertScrappedData
}
Relevant documentation
This is actually a condensed form of assigning the getAllProducts function to the getAllProducts key of a new object and then assigning that object to module.exports
// Equivalent but unnecessarily verbose
module.exports = {
getAllProducts: getAllProducts,
InsertScrappedData: InsertScrappedData
}