Skip to content

Scalar arrays nested in plain objects are typed as JSON instead of typed lists under Mongoose 9 #449

Description

@ochiribogaz

Summary

Under Mongoose 9, composeMongoose types scalar arrays nested inside a plain (non-subdocument) object as the JSON scalar instead of a typed GraphQL list (e.g. [Float]). Top-level scalar arrays and arrays of subdocuments are unaffected. The same models produce correctly-typed lists under Mongoose 8, so this is specific to the Mongoose 9 .caster removal.

I searched the open/closed issues and didn't find an existing report — apologies if I missed one.

Minimal reproduction

const mongoose = require('mongoose');
const { composeMongoose } = require('graphql-compose-mongoose');
const { schemaComposer } = require('graphql-compose');

const schema = new mongoose.Schema({
  location: { type: { type: String, default: 'Point' }, coordinates: [Number] }, // scalar array nested in a plain object
  score: { sets: [[{ type: Number }]], comments: [String] },
  topLevelArr: [Number], // control: top-level scalar array
});

const TC = composeMongoose(mongoose.model('Probe', schema), { schemaComposer });
console.log(TC.toSDL({ deep: true, exclude: ['MongoID'] }));

Expected (Mongoose 8)

type Probe { topLevelArr: [Float] }
type ProbeLocation { coordinates: [Float] }
type ProbeScore { sets: [[Float]]  comments: [String] }

Actual (Mongoose 9)

type Probe { topLevelArr: [Float] }     # top-level array: still correct
type ProbeLocation { coordinates: JSON }    # nested-in-object array: wrong
type ProbeScore { sets: JSON  comments: JSON }

The control (topLevelArr) shows it's specifically the nested-in-an-object case that breaks.

Root cause

In src/fieldsConverter.ts (_getFieldComplexType), the array branch only recognizes .caster:

} else if (field instanceof mongoose.Schema.Types.Array || field?.caster?.instance) {
  return ComplexTypes.ARRAY;
}

Mongoose 9 removed SchemaType.caster in favor of embeddedSchemaType. For an array unwrapped out of a plain object, the instanceof check doesn't fire and field.caster is undefined, so it falls through to SCALAR and scalarToGraphQL emits the JSON fallback.

arrayToGraphQL was already updated for this (field.caster || field.embeddedSchemaType), but the classifier above wasn't — so it never reaches arrayToGraphQL.

Suggested fix

Add the embeddedSchemaType fallback to the classifier:

} else if (
  field instanceof mongoose.Schema.Types.Array ||
  field?.caster?.instance ||
  field?.embeddedSchemaType?.instance
) {
  return ComplexTypes.ARRAY;
}

This keeps Mongoose 8 working (via .caster) and fixes Mongoose 9 (via .embeddedSchemaType); it only matches array fields, so other field types are unaffected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions