Solving NestJS TypeError: Converting circular structure to JSON

Updated: December 31, 2023 By: Guest Contributor Post a comment

When working with Node.js and NestJS, developers might occasionally encounter an error message that reads ‘NestJS TypeError: Converting circular structure to JSON’. This error typically occurs when you try to convert a circular data structure into a JSON string, something which JSON.stringify() cannot handle due to the nature of JSON.

Understanding the Circular Structure Error

In JavaScript, a circular reference occurs when an object refers back to itself. This is common when creating relationships between objects or entities. For example, in a parent-child relationship, a parent may reference a child, which in turn has a reference back to the parent. When attempting to serialize (convert to a string) an object with circular references, JSON.stringify() will throw the mentioned error because it ends up in an infinite loop trying to resolve the references.

Using Class-transformer Library

One common solution to dealing with circular references in NestJS is to use the ‘class-transformer’ library. The library provides decorators that allow for custom transformation logic when serializing and deserializing objects. To resolve the circular structure error, we can implement a custom transformation that ignores the circular references.

First, make sure to install the class-transformer package if it’s not already part of your project:

npm install class-transformer

Next, let’s consider the following code example where a circular reference is likely:

import { Exclude, Expose } from 'class-transformer';

class Parent {
    child: Child;
}

class Child {
    @Exclude()
    parent: Parent;

    @Expose()
    getName(): string {
        return this.name;
    }
}

let parent = new Parent();
let child = new Child();
parent.child = child;
child.parent = parent;

In the above example, we’re creating two classes: Parent and Child. Each class references the other, creating a circular reference. By using the @Exclude() decorator from class-transformer, we tell the library to ignore the ‘parent’ property in the Child class when serializing the object. Note that the @Expose() decorator is optional but used here to highlight that the getName method should be included in the serialized result.

When you want to serialize the object, you’d use the classToPlain function from class-transformer:

import { classToPlain } from 'class-transformer';

let serializedObj = classToPlain(child);
console.log(JSON.stringify(serializedObj));

This will serialize the Child object without the parent property, preventing the circular structure error.

Design Circular References Mindfully

As a long-term approach, it’s usually better to design your data structures to avoid circular references where possible. If your use case allows, consider restructuring your data so that it retains a hierarchical model, without cross-references. This would natively solve the issue when serializing objects to JSON.

In cases where a simple structure isn’t possible or ideal, make sure to apply a thoughtful approach on how and when objects should reference each other. Sometimes a middle-ground can be found by using identifiers (such as database IDs) to reference related objects instead of holding a direct reference to the entire object in-memory.

Custom toJSON Method

JavaScript objects can have a custom toJSON method that JSON.stringify() will call when serializing the object. This method can be designed to deal with circular references:

class Parent {
    child: Child;

    toJSON() {
        const { child, ...otherProps } = this;
        return otherProps;
    }
}

class Child {
    parent: Parent;

    toJSON() {
        const { parent, ...otherProps } = this;
        return otherProps;
    }
}

let parent = new Parent();
let child = new Child();
parent.child = child;
child.parent = parent;

console.log(JSON.stringify(parent));

In each class’s toJSON method, we destructure the object to separate the circular reference from the other properties. We then return the rest of the properties, effectively excluding the problematic reference from serialization. In this way, JSON.stringify() no longer throws an error, as it uses our custom toJSON method which avoids the circular reference.

Ultimately, dealing with the NestJS TypeError for circular structures involves either excluding the circular reference at serialization time, restructuring the data, or providing custom serialization logic. With these methods, you can ensure that your application can handle and serialize complex object relationships without errors.