class-validator-jsonschema icon indicating copy to clipboard operation
class-validator-jsonschema copied to clipboard

Don't use refs for nested objects

Open dolencd opened this issue 2 years ago • 3 comments

Hi!

I have several database models defined using class-validator and I'd like to add validation inside the database itself by converting the existing models to JSONSchema. Some converters need to be changed (bsonType), but that's manageable by changing some converters.

One thing that I haven't figured out how to do is to store nested documents in this way. The default behavior of class-validator-jsonschema is to create a ref, but MongoDB doesn't support this.

Instead of creating refs I just need all the schemas to be nested. As far as I know there's currently no way to do this, but it feels like something that could be part of the config. Are there any plans for anything like this?

dolencd avatar Jun 23 '23 12:06 dolencd

A workaround, you can do this with a custom converter:

const options: Partial<IOptions> = {
  additionalConverters: {
    [cv.ValidationTypes.NESTED_VALIDATION]: (meta, options) => {
      if (typeof meta.target === `function`) {
        const typeMeta = options.classTransformerMetadataStorage
          ? options.classTransformerMetadataStorage.findTypeMetadata(
              meta.target,
              meta.propertyName,
            )
          : null;
        const childType = typeMeta
          ? typeMeta.typeFunction()
          : getPropType(meta.target.prototype, meta.propertyName);
        
        return targetConstructorToSchema(childType, options);
      }
    },
  },
};

Noting that depending on your setup, you'll probably need to tweek your class decorators and options: https://github.com/epiphone/class-validator-jsonschema#validatenested-and-arrays.

barlock avatar Jun 26 '23 21:06 barlock

I see, basically recursively populating nested fields by just running the conveter again for that sub type. Thank you!

dolencd avatar Jun 29 '23 15:06 dolencd

I had boot issues with const childType part of the barock's snippet, but the edited one below works pretty decent for my use case of nesting a simple object:

classTransformerMetadataStorage: defaultMetadataStorage,
additionalConverters: {
      [ValidationTypes.NESTED_VALIDATION]: (meta, options) => {
        if (typeof meta.target === "function") {
          const typeMeta = options.classTransformerMetadataStorage
              ? options.classTransformerMetadataStorage.findTypeMetadata(
                  meta.target,
                  meta.propertyName,
              )
              : null;
          if (typeMeta) {
            const childType = typeMeta.typeFunction()
            return targetConstructorToSchema(childType, options)
          }
        }
        return {}
      }
    }

nikola-susa avatar Sep 22 '23 22:09 nikola-susa