graphql-compose / graphql-compose/graphql-compose-mongoose

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

Open Beginner friendly
#449 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
706
Forks
98
PR merge metrics
No merged PRs in 30d

Description

## 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

```js
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)

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

## Actual (Mongoose 9)

```graphql
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`:

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

[Mongoose 9 removed `SchemaType.caster`](https://mongoosejs.com/docs/migrating_to_9.html#schematype-caster-and-casterconstructor-properties-were-removed) 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:

```ts
} 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.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in src/fieldsConverter.ts at _getFieldComplexType and compare its array classification with the existing arrayToGraphQL handling. Use the provided Mongoose schema reproduction and inspect the generated SDL; done means nested scalar arrays produce typed GraphQL lists while top-level arrays and Mongoose 8 behavior remain correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.