graphql-compose / graphql-compose/graphql-compose-mongoose
Scalar arrays nested in plain objects are typed as JSON instead of typed lists under Mongoose 9
- 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