How can I remove cyclic structure in JSON? [duplicate] - javascript

In my react native expo project I am facing an error that says "TypeError: JSON.stringify cannot serialize cyclic structures. stringify#[native code]" Can anyone can help me to fix this problem? i tried using a library called "json-stringify-safe" but after using it like this "body: jsonStringifySafe(MessageData)," it was giving me react-navigation error Can anyone can help me to fix this error?
const SendMessage = async () => {
const MessageData = {
message: currentmessage,
RoomId: roomid,
SenderId: mydata._id,
RecieverId: otheruser[0]._id
};
fetch('http://10.0.2.2:3000/saveMessage', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(MessageData),
})
.then(res => res.json())
.then(data => {
if (data.message === "Message Saved!") {
console.log("Message Saved!");
setCurrentMessage('');
} else {
alert("Please, Try Again");
}
});
};
My Backend:
router.post("/saveMessage", async (req, res) => {
const { SenderId, RecieverId, message, RoomId } = req.body;
try {
const NewMessage = new Message({
SenderId,
RecieverId,
message,
RoomId
})
await NewMessage.save();
res.send("Message Saved!")
}
catch (err) {
console.log('error while saving message', err);
res.status(422).send(err.message);
}
})

The error is telling you that you have a property on MessageData (or their descendants) that directly or indirectly end up referring to themselves. For instance:
const parent = { children: [] };
const child = { parent };
parent.children.push(child);
At this point, parent refers to child which refers to parent. If you did JSON.stringify on either of them (directly or indirectly), you'd get this error because JSON can't represent cyclical structures:
const parent = { children: [] };
const child = { parent };
parent.children.push(child);
console.log(JSON.stringify(parent));
So you'll have to look at MessageData and the objects it refers to to find out where the cycle is. Note that it could be quite deep down:
const parent = { children: [] };
const child = { parent };
parent.children.push(child);
const zero = {
one: {
two: {
three: {
parent
},
},
},
};
console.log(JSON.stringify(zero));
Some JavaScript engines offer you more information about the cyclic structure than others. For instance, here's what V8 (the engine used by Chromium browsers and Node.js) says about the above:
js:26 Uncaught TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'children' -> object with constructor 'Array'
| index 0 -> object with constructor 'Object'
--- property 'parent' closes the circle
at JSON.stringify ()
at js:26:18
That's quite informative, more so than what you've quoted in your question. So you might try using a Chromium browser if you aren't already to replicate the issue in hopes it can give you more detail about where the cycle is.

Related

TypeError: JSON.stringify cannot serialize cyclic structures. stringify#[native code]

In my react native expo project I am facing an error that says "TypeError: JSON.stringify cannot serialize cyclic structures. stringify#[native code]" Can anyone can help me to fix this problem? i tried using a library called "json-stringify-safe" but after using it like this "body: jsonStringifySafe(MessageData)," it was giving me react-navigation error Can anyone can help me to fix this error?
const SendMessage = async () => {
const MessageData = {
message: currentmessage,
RoomId: roomid,
SenderId: mydata._id,
RecieverId: otheruser[0]._id
};
fetch('http://10.0.2.2:3000/saveMessage', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(MessageData),
})
.then(res => res.json())
.then(data => {
if (data.message === "Message Saved!") {
console.log("Message Saved!");
setCurrentMessage('');
} else {
alert("Please, Try Again");
}
});
};
My Backend:
router.post("/saveMessage", async (req, res) => {
const { SenderId, RecieverId, message, RoomId } = req.body;
try {
const NewMessage = new Message({
SenderId,
RecieverId,
message,
RoomId
})
await NewMessage.save();
res.send("Message Saved!")
}
catch (err) {
console.log('error while saving message', err);
res.status(422).send(err.message);
}
})
The error is telling you that you have a property on MessageData (or their descendants) that directly or indirectly end up referring to themselves. For instance:
const parent = { children: [] };
const child = { parent };
parent.children.push(child);
At this point, parent refers to child which refers to parent. If you did JSON.stringify on either of them (directly or indirectly), you'd get this error because JSON can't represent cyclical structures:
const parent = { children: [] };
const child = { parent };
parent.children.push(child);
console.log(JSON.stringify(parent));
So you'll have to look at MessageData and the objects it refers to to find out where the cycle is. Note that it could be quite deep down:
const parent = { children: [] };
const child = { parent };
parent.children.push(child);
const zero = {
one: {
two: {
three: {
parent
},
},
},
};
console.log(JSON.stringify(zero));
Some JavaScript engines offer you more information about the cyclic structure than others. For instance, here's what V8 (the engine used by Chromium browsers and Node.js) says about the above:
js:26 Uncaught TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'children' -> object with constructor 'Array'
| index 0 -> object with constructor 'Object'
--- property 'parent' closes the circle
at JSON.stringify ()
at js:26:18
That's quite informative, more so than what you've quoted in your question. So you might try using a Chromium browser if you aren't already to replicate the issue in hopes it can give you more detail about where the cycle is.

TypeError: is not a function with React, Typescript and Axios

So, I searched for an existing solution, but I could find nothing, or maybe I'm not searching the correct way, thus, sorry if there's an existing thread about it.
In sum, it seems my code is not instantiating an object correctly as a class when it comes from an Axios call to the backend. So, when I call some function, I'm getting the error Uncaught TypeError TypeError: object.method is not a function.
Example:
First, basically, a parent component will call a service that will make a request to the backend. The result is then passed to a child component.
// imports
const Component: React.FC<ComponentProps> = () => {
const { id } = useParams<{ id: string }>();
const [object, setObject] = useState<Class>(new Class());
useEffect(() => {
(async () => {
try {
const object = await Service.getById(id);
setObject(object);
} catch (err) {
//error handling
} finally {
cleanup();
}
})();
return () => {
// cleanup
};
});
return (
<Container title={object.name}>
<Child object={object} />
</Container>
);
};
export default Component;
Then, in child component, let's say I try to call a method that was defined in the Class, there I'm getting the not a function error:
// imports
interface Interface {
object: Class;
}
const Child: React.FC<Interface> = ({ object }) => {
object.callSomeFunction(); // error starts here
return (
<SomeJSXCode />
);
};
export default Child;
Example of the Class code, I tried to write the method as a function, arrow function, and a getter, but none worked. Also, as a workaround, I've been defining a method to instantiate the object and set all properties, but I don't think that's a good long-term solution, and for classes with many properties, it gets huge:
export class Class {
id: string = '';
name: string = '';
callSomeFunction = () => {
// do something;
}
static from(object: Class): Class {
const newInstance = new Class();
newInstance.id = object.id;
newInstance.name = object.name;
// imagine doing this for a class with many attributes
return newInstance;
}
}
Finally, the Service code if necessary to better understand:
// imports
const URL = 'http://localhost:8000';
const baseConfig: AxiosRequestConfig = {
baseURL: URL,
headers: { 'Content-Type': 'application/json' },
withCredentials: true,
};
export const backend = axios.create({
...baseConfig,
baseURL: URL + '/someEndpoint',
});
export const Service = {
async getById(id: string): Promise<Class> {
try {
const { data } = await backend.get<Class>(`/${id}`);
return data;
} catch (err) {
throw new Error(err.response.data.message);
}
},
};
As I can't share the real code due to privacy, please let me know if this is enough or if more information is needed. Thanks in advance.
I thought it was some binding issue as here, but no.
So, I actually fixed this by updating the class validator in the back end, as the parsing was only necessary to parse the strings as number. But, by adding the annotation #Type(() => Number) to my dtos, I won't need to parse the strings anymore.

Variable "$id" got invalid value "1"; Int cannot represent non-integer value: "1"

I've been learning the mern stack from this book
I'm now on Nested Routes under React Router chapter
The web application is supposed to render this on the page.
When clicking the Select link under the Action column, the description of
an issue is displayed on the bottom part of the page.
But in my case, this thing happens:
and at the same time this error is being thrown in the console:
The only time the web application runs properly is when I downgraded the
graphql version to 0.13.2 (this is the version the book uses).
The thing is I try to use up to date versions of the project dependencies
as much as possible. There has never been much trouble as I follow the book
until I got into this.
I don't understand, why is this error being thrown when I use a more up to
date version of the graphql over the old version?
(I use graphql version 15.8.0 and apollo-server-express version 2.25.4)
I tried to modify the .jsx file that renders the description data
on the page.
async loadData() {
const { match: { params: { id } } } = this.props;
//I tried to parse the id to make it an int type before getting it into
//the graphql query
id = parseInt(id); // this is the thing that I've added
const query = `query issue($id: Int!) {
issue (id: $id) {
id description
}
}`;
const data = await graphQLFetch(query, { id });
if (data) {
this.setState({ issue: data.issue });
} else {
this.setState({ issue: {} });
}
}
This is the codes graphQLFetch function
const dateRegex = new RegExp('^\\d\\d\\d\\d-\\d\\d-\\d\\d');
function jsonDateReviver(key, value) {
if (dateRegex.test(value)) return new Date(value);
return value;
}
async function graphQLFetch(query, variables = {}) {
try {
const response = await fetch(window.ENV.UI_API_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables }),
});
const body = await response.text();
const result = JSON.parse(body, jsonDateReviver);
if (result.errors) {
const error = result.errors[0];
if (error.extensions.code === 'BAD_USER_INPUT') {
const details = error.extensions.exception.errors.join('\n');
alert(`${error.message}:\n ${details}`);
} else {
alert(`${error.extensions.code}: ${error.message}`);
}
}
return result.data;
} catch (e) {
alert(`Error in sending data to server: ${e.message}`);
return null;
}
}
When I did this, it doesn't throw any error anymore but it doesn't render
the description data on the page either.
Can someone please help me with this?? Thanks in advance...

how to access apollo client error object properties

I'm fetching a query to a GraphQL server that throws an error if something goes wrong. Now, I want to have access to that error but instead Apollo Client is showing me the general Error: Network error: Response not successful: Received status code 500.
By adding the onError property to my query, I can see the Error object in the console but only if it's wrapped in {}, otherwise it's just text.
This is my code:
const { data, loadMore, loading, error, retry } = useRetryableQuery<
GetDevices,
DevicesQueryVariables
>(devicesQueries.GetDevices, {
onError: (networkError) => {
console.log(networkError); //this will show me the error as a whole text and not an object
console.log({ networkError }) // this will show me an object which its properties I cannot access
},
errorPolicy: "all",
notifyOnNetworkStatusChange: true,
paginationPath: ["paging"],
itemsPaths: [["filteredDevices", "rows"]],
variables: {
...queryVariables,
connectionPaging: {
offset: 0,
limit: PAGE_SIZE,
},
},
});
How can I access the properties nested inside the networkError object?
First screenshot is without {}
Second one is with {}
Thanks in advance!
The issue was related to this Apollo Client issue: useQuery() refetch throws on error instead of flowing through hook, so getting the error from the ApolloProvider directly allowed me to access the error sent from the backend:
const errorLink = useMemo(
() =>
onError((error) => {
const message = getApolloError({ graphQLErrors: error.graphQLErrors
});
if (message) {
localStorage.setItem("error", message);
} else {
localStorage.removeItem("error");
}
if (message === "Session expired.") {
logout(true);
}
}),
[logout]
);
const client = useMemo(
() =>
new ApolloClient({
cache,
link: ApolloLink.from([errorLink, authLink, httpLink]),
defaultOptions: {
watchQuery: {
fetchPolicy: "network-only",
},
query: {
fetchPolicy: "network-only",
},
},
}),
[httpLink, errorLink]
);

Create a nested return model with Knex.js

I'm using Knex.js to query a MySQL database in a Hapi.js route. The following code works but requires a nested query:
{
path: '/recipes',
method: 'GET',
handler: (req, res) => {
const getOperation = Knex.from('recipes')
// .innerJoin('ingredients', 'recipes.guid', 'ingredients.recipe')
.select()
.orderBy('rating', 'desc')
.limit(10)
.then((recipes) => {
if (!recipes || recipes.length === 0) {
res({
error: true,
errMessage: 'no recipes found'
});
}
const recipeGuids = recipes.map(recipe => recipe.guid);
recipes.forEach(r => r.ingredients = []);
const getOperation2 = Knex.from('ingredients')
.whereIn('recipe', recipeGuids)
.select()
.then((ingredients) => {
recipes.forEach(r => {
ingredients.forEach(i => {
if (i.recipe === r.guid) {
r.ingredients.push(i);
}
});
});
res({
count: recipes.length,
data: recipes
});
});
});
}
}
Is there a way to create a return model with Knex.js that has nested objects that match the parent's id/guid so that I don't have nested promises?
Short answer: No.
With Knex, you can retrieve data the same as with SQL, which is record based, not object based, so the closest that you could come would be to use a join to allow doing just a single select to retrieve a single array having elements: recipes, guids, ingredients. This would repeat the recipe & guid for each ingredient, which you avoid by using nested objects. (See the answer below by #Fazal for an example of this.)
As another alternative, you could store the ingredients as a 'blob' field in the recipe table, but I don't believe that MySQL would allow you to create an Array field, so when retrieving the data, you would have to do a transform of the field into the array. And transform it from the Array before updating it into the table. Like: storableData = JSON.stringify(arrayData) and arrayData = JSON.parse(storableData)
There are a few other things that I would suggest to help you improve the code though. (Yeah, I know, not really the question here):
Separate the routing functionality from data handling.
Also, separate data manipulation functionality from retrieval.
Use throw & .catch for creating and handling unsuccessful responses.
The separation of routing, data retrieval, data manipulation makes testing, debugging, and future comprehension easier as each function has a more atomic purpose.
Throwing/.catching unsuccessful process conditions makes it much simpler to have more comprehensive error processing by allowing you to put (most of the time) a single .catch in your router response handling (Hapi.js may even do this .catch for you???).
Also, see the other .catch and .on('query-error' that I added for logging errors. You may have a different logging mechanism you want to use rather than the console. I use Winston. And note that .on('query-error' is NOT a .catch. There will still be an Error() that is thrown, and must be handled somewhere, this will just give you good info about the failure close to the source.
(Sorry, the below code is untested)
path: '/recipes',
method: 'GET',
handler: (req, res) => {
return getRecipeNIngredients()
.then((recipes) => {
res({
count: recipes.length,
data: recipes
});
})
.catch((ex) => {
res({
error: true,
errMessage: ex.message
});
});
};
function getRecipeNIngredients() {
let recipes = null;
return getRecipes()
.then((recipeList) => {
recipes = recipeList;
const recipeGuids = recipes.map(recipe => recipe.guid);
recipes.forEach(r => r.ingredients = []);
return getIngredients(recipeGuids);
})
.then((ingredients) => {
recipes.forEach(r => {
ingredients.forEach(i => {
if (i.recipe === r.guid) {
r.ingredients.push(i);
}
});
});
return recipes;
})
.catch((ex) => {
console.log(".getRecipeNIngredients ERROR ex:",ex); // log and rethrow error.
throw ex;
});
};
function getRecipes() {
return Knex.from('recipes')
// .innerJoin('ingredients', 'recipes.guid', 'ingredients.recipe')
.select()
.orderBy('rating', 'desc')
.limit(10)
.on('query-error', function(ex, obj) {
console.log("KNEX getRecipes query-error ex:", ex, "obj:", obj);
})
.then((recipes) => {
if (!recipes || recipes.length === 0) {
throw new Error('no recipes found')
}
})
};
function getIngredients(recipeGuids) {
return Knex.from('ingredients')
.whereIn('recipe', recipeGuids)
.select()
.on('query-error', function(ex, obj) {
console.log("KNEX getIngredients query-error ex:", ex, "obj:", obj);
})
};
I hope this is Useful!
Gary.
I created a library that return nested object even it has types for typescript
Nested Knex
import * as n from 'nested-knex';
n.array(
n.type({
id: n.number("recipe.id", { id: true }),
title: n.string("recipe.title"),
ingredients: n.array(
n.type({
id: n.number("ingredients.id", { id: true }),
title: n.string("ingredients.title")
})
)
})
)
.withQuery(
knex
.from("recipes")
.innerJoin("ingredients", "recipes.guid", "ingredients.recipe")
.select()
.orderBy("rating", "desc")
.limit(10)
)
.then(recipes => {});
so recipes even have types
You can easily avoid nest query. Just use subquery as-
knex.select('*')
.from(function () {
this.select('*').from('recipes').limit(10).as('recipes'); // limit here
})
.leftJoin('ingredients', 'ingredients.recipe_id', 'recipes.guid')
.then((rec) => {
console.log(rec);
})
see.. just few lines of code.

Categories