|
| 1 | +# Release notes graphql-compose-mongoose v9.0.0 |
| 2 | + |
| 3 | +<!-- TOC depthFrom:2 --> |
| 4 | + |
| 5 | +- [What's new?](#whats-new) |
| 6 | + - [🛑 resolver creation (factory)](#🛑-resolver-creation-factory) |
| 7 | + - [🛑 error handling (payload.error, mongoose validation)](#🛑-error-handling-payloaderror-mongoose-validation) |
| 8 | +- [Improvements](#improvements) |
| 9 | + - [Now `_id` field can be of any type (Int, String, Object)](#now-_id-field-can-be-of-any-type-int-string-object) |
| 10 | + - [🛑 recursive `filter._operators`](#🛑-recursive-filter_operators) |
| 11 | + - [🛑 better alias & projection support for nested embedded documents](#🛑-better-alias--projection-support-for-nested-embedded-documents) |
| 12 | + - [🛑 typescript improvements for resolvers](#🛑-typescript-improvements-for-resolvers) |
| 13 | +- [Performance improvements](#performance-improvements) |
| 14 | + - [Added new `dataLoader` & `dataLoaderMany` resolvers](#added-new-dataloader--dataloadermany-resolvers) |
| 15 | + - [Add `lean: boolean` option to query resolvers](#add-lean-boolean-option-to-query-resolvers) |
| 16 | +- [Breaking changes](#breaking-changes) |
| 17 | + - [In resolver `updateById` was changed its input args](#in-resolver-updatebyid-was-changed-its-input-args) |
| 18 | + - [Some generated types were renamed](#some-generated-types-were-renamed) |
| 19 | +- [Misc](#misc) |
| 20 | +- [Thanks](#thanks) |
| 21 | + - [Thanks to contributors](#thanks-to-contributors) |
| 22 | + - [Thanks to sponsors](#thanks-to-sponsors) |
| 23 | + |
| 24 | +<!-- /TOC --> |
| 25 | + |
| 26 | +## What's new? |
| 27 | + |
| 28 | +### 🛑 resolver creation (factory) |
| 29 | + |
| 30 | +- Refactor resolver creation #263 |
| 31 | +- Expose `mongooseResolvers` for manual creation of resolvers #274 |
| 32 | +- feat: expose `resolverFactory` for manual resolver creation, eg. for cloned types: `resolverFactory.findMany(UserModel, ClonedUserTC, opts)` – it generates Resolver's types according to `ClonedUserTC` type and will use mongoose `UserModel` for making requests to DB |
| 33 | +- feat: added new function `composeMongoose` which generates TypeComposer without resolvers. Now Resolvers can be generated on-demand in such way `PostTC.mongooseResolvers.findMany()` (before was `PostTC.getResolver('findMany')`) |
| 34 | +- Improve Typescript definitions for resolvers |
| 35 | + |
| 36 | +### 🛑 error handling (payload.error, mongoose validation) |
| 37 | + |
| 38 | +- Add `error` field & handler for mutations' payload #248 |
| 39 | +- Resolvers createOne createMany now returns validation errors |
| 40 | + |
| 41 | +## Improvements |
| 42 | + |
| 43 | +### Now `_id` field can be of any type (Int, String, Object) |
| 44 | + |
| 45 | +Before v9.0.0 was supported only `MongoID` type for `_id` field. Now it can be of any type – Int, String, Object. For using this feature you need to add `_id` field to mongoose schema with desired type and graphql-compose-mongoose will do the rest: |
| 46 | + |
| 47 | +```ts |
| 48 | +const BookSchema = new mongoose.Schema({ |
| 49 | + _id: { type: Number }, |
| 50 | + title: { type: String }, |
| 51 | +}); |
| 52 | + |
| 53 | +interface IBook extends Document { |
| 54 | + _id: number; |
| 55 | + title?: string; |
| 56 | +} |
| 57 | + |
| 58 | +const BookModel = mongoose.model<IBook>('Book', BookSchema); |
| 59 | +const BookTC = composeMongoose(BookModel); |
| 60 | +``` |
| 61 | + |
| 62 | +Notes: |
| 63 | + |
| 64 | +- If you choose type `Number` for `_id` field then `graphql-compose-mongoose` will cast it to `Int` GraphQL type. For other fields `Number` is casted to `Float` by default. Anyway you able change type manually – `BookTC.extendField('_id', { type: 'Float!' })`. |
| 65 | +- Be careful: Mongoose will refuse to save a document that doesn't have an `_id`. So you're responsible for setting `_id` if you define your own `_id` path. For automatic numeric id creation you can use the following plugins [mongoose-plugin-autoinc](https://www.npmjs.com/package/mongoose-plugin-autoinc) or [@typegoose/auto-increment](https://www.npmjs.com/package/@typegoose/auto-increment). |
| 66 | + |
| 67 | +[Issue #141](https://github.com/graphql-compose/graphql-compose-mongoose/issues/141) |
| 68 | + |
| 69 | +### 🛑 recursive `filter._operators` |
| 70 | + |
| 71 | +- Adds support for recursive `filter._operators` (rename its type names ) #250 |
| 72 | + |
| 73 | +### 🛑 better alias & projection support for nested embedded documents |
| 74 | + |
| 75 | +- Nested projections for embedded documents #273 |
| 76 | +- feat: nested aliases are now supported for `filter`, `projection` & `lean` |
| 77 | + |
| 78 | +### 🛑 typescript improvements for resolvers |
| 79 | + |
| 80 | +- source is now typed, and first level of available args |
| 81 | + |
| 82 | +## Performance improvements |
| 83 | + |
| 84 | +### Added new `dataLoader` & `dataLoaderMany` resolvers |
| 85 | + |
| 86 | +These resolvers are helpful for relations construction between Entities for avoiding the N+1 Problem via [DataLoader](https://github.com/graphql/dataloader). This problem occurs when a client requests an array of records with some relation data: |
| 87 | +- GraphQL call first resolve method with 1 query for getting a list of records |
| 88 | +- and for every record will call nested resolve methods which make separate DB requests |
| 89 | + |
| 90 | +As you can expect, doing N+1 queries will flood your database with queries, which is something we can and should avoid. So `dataLoader`, `dataLoaderMany` resolvers make one batch request for getting all related records by id. |
| 91 | + |
| 92 | +```ts |
| 93 | +import { schemaComposer } from 'graphql-compose'; |
| 94 | +import { composeMongoose } from 'graphql-compose-mongoose'; |
| 95 | +import { mongoose, Document } from 'mongoose'; |
| 96 | + |
| 97 | +mongoose.set('debug', true); // <-- show mongoose queries in console |
| 98 | + |
| 99 | +const UserSchema = new mongoose.Schema({ |
| 100 | + name: { type: String, required: true }, |
| 101 | +}); |
| 102 | +const PostSchema = new mongoose.Schema({ |
| 103 | + title: { type: String, required: true }, |
| 104 | + authorId: { type: mongoose.Types.ObjectId }, |
| 105 | + reviewerIds: { type: [mongoose.Types.ObjectId] }, |
| 106 | +}); |
| 107 | + |
| 108 | +interface IUser extends Document { |
| 109 | + name: string; |
| 110 | +} |
| 111 | + |
| 112 | +interface IPost extends Document { |
| 113 | + title: string; |
| 114 | + authorId?: mongoose.Types.ObjectId; |
| 115 | + reviewerIds?: [mongoose.Types.ObjectId]; |
| 116 | +} |
| 117 | + |
| 118 | +const UserModel = mongoose.model<IUser>('User', UserSchema); |
| 119 | +const PostModel = mongoose.model<IPost>('Post', PostSchema); |
| 120 | + |
| 121 | +const UserTC = composeMongoose(UserModel); |
| 122 | +const PostTC = composeMongoose(PostModel); |
| 123 | + |
| 124 | +PostTC.addRelation('author', { |
| 125 | + // resolver: () => UserTC.mongooseResolvers.findById({ lean: true }), |
| 126 | + resolver: () => UserTC.mongooseResolvers.dataLoader({ lean: true }), |
| 127 | + prepareArgs: { |
| 128 | + _id: (s) => s.authorId, |
| 129 | + }, |
| 130 | + projection: { authorId: true }, |
| 131 | +}); |
| 132 | + |
| 133 | +PostTC.addRelation('reviewers', { |
| 134 | + // resolver: () => UserTC.mongooseResolvers.findByIds({ lean: true }), |
| 135 | + resolver: () => UserTC.mongooseResolvers.dataLoaderMany({ lean: true }), |
| 136 | + prepareArgs: { |
| 137 | + _ids: (s) => s.reviewerIds, |
| 138 | + }, |
| 139 | + projection: { reviewerIds: true }, |
| 140 | +}); |
| 141 | + |
| 142 | +schemaComposer.Query.addFields({ |
| 143 | + posts: PostTC.mongooseResolvers.findMany(), |
| 144 | +}); |
| 145 | + |
| 146 | +// console.log(schemaComposer.toSDL()); |
| 147 | +export const schema = schemaComposer.buildSchema(); |
| 148 | +``` |
| 149 | + |
| 150 | +Test suite for this example can be found [here](https://github.com/graphql-compose/graphql-compose-mongoose/blob/master/src/__tests__/github_issues/260-test.ts). |
| 151 | + |
| 152 | +### Add `lean: boolean` option to query resolvers |
| 153 | + |
| 154 | +Resolvers with `lean: true` is significantly faster than without it (anywhere from 3 - 10 times faster). If you need just `raw` data from DB then use this option. By default queries return fully instantiated Mongoose documents for supporting mongoose's `virtuals` fields, plugins and model methods (but it consume much more CPU & RAM). |
| 155 | + |
| 156 | +`lean` option available in the following resolvers `findById`, `findByIds`, `findMany`, `findOne`, `dataLoader`, `dataLoaderMany`. |
| 157 | + |
| 158 | +BTW mongoose `aliases` are supported with `lean: true` option. graphql-compose-mongoose takes care about their proper conversion in filters, projection and output results: |
| 159 | + |
| 160 | +```ts |
| 161 | +// With aliases in MongoDB you will have such records |
| 162 | +// { _id: '...', n: 'John', a: 26 } |
| 163 | +const AuthorSchema = new mongoose.Schema({ |
| 164 | + name: { type: String, alias: 'n' }, |
| 165 | + score: { type: Number, alias: 's' }, |
| 166 | +}); |
| 167 | +const AuthorModel = mongoose.model<IAuthor>('Author', AuthorSchema); |
| 168 | + |
| 169 | +// Will be generate graphql type with full field names |
| 170 | +// type Author { name: String, score: Float } |
| 171 | +const AuthorTC = composeMongoose(AuthorModel, { schemaComposer }); |
| 172 | + |
| 173 | +// Resolver will send queries something like that: |
| 174 | +// db.author.find({ n: 'John' }) |
| 175 | +// And convert shortened raw records to full form |
| 176 | +// { _id: '...', n: 'John', s: 26 } |
| 177 | +const userFindManyResolver = AuthorTC.mongooseResolvers.findMany({ lean: true }); |
| 178 | +``` |
| 179 | + |
| 180 | +- feat add `lean: true` option #259, #266 [commit](https://github.com/graphql-compose/graphql-compose-mongoose/commit/321dded4e2b346e5ab3d549a6bc5b31458478fd1) |
| 181 | + |
| 182 | +## Breaking changes |
| 183 | + |
| 184 | +### In resolver `updateById` was changed its input args |
| 185 | + |
| 186 | +From `UpdateByIdRecord` input type was extracted `_id` field on top level. |
| 187 | + |
| 188 | +```diff |
| 189 | +- updateById(record: UpdateByIdRecord!) |
| 190 | ++ updateById(_id: MongoID!, record: UpdateByIdRecord!) |
| 191 | +``` |
| 192 | + |
| 193 | +[Issue #257](https://github.com/graphql-compose/graphql-compose-mongoose/issues/257) |
| 194 | + |
| 195 | +### Some generated types were renamed |
| 196 | + |
| 197 | +- type for `filter._operators` field. Was `OperatorsXXXFilterInput` became `XXXFilterOperatorsInput`. It helps to keep all generated types with the same prefix for `XXX` entity. |
| 198 | +- in resolver `count` was changed `filter` type name from `Filter` to `FilterCount`. All rest resolvers already had `FilterFindMany`, `FilterFinOne`, etc. names, and only `count` resolver does not follow this pattern. |
| 199 | + |
| 200 | +## Misc |
| 201 | + |
| 202 | +- Refactor `pagination` & `connection` resolvers (now they are as dependencies) [#272](https://github.com/graphql-compose/graphql-compose-mongoose/issues/272) |
| 203 | +- Allow to provide `suffixes` for resolvers configs [#268](https://github.com/graphql-compose/graphql-compose-mongoose/issues/268) |
| 204 | +- Remove `getRecordIdFn()` [#262](https://github.com/graphql-compose/graphql-compose-mongoose/issues/262) |
| 205 | + |
| 206 | +## Thanks |
| 207 | + |
| 208 | +### Thanks to contributors |
| 209 | + |
| 210 | +It will not be possible to provide such great improvements in v9.0.0 without the following amazing peoples: |
| 211 | + |
| 212 | +- [Robert Lowe](@RobertLowe) – new improved error payload for Mutations and better validation Errors on document creating/updating. |
| 213 | +- [Sean Campbell](natac13) – nested projection for reducing the amount of transmitted data from DB. |
| 214 | +- [Morgan Touverey Quilling](@toverux) – non-nullability for fields with default values, help in lean resolvers. |
| 215 | + |
| 216 | +Thank you very much for your help 🙏 |
| 217 | + |
| 218 | +### Thanks to sponsors |
| 219 | + |
| 220 | +Special thanks to our sponsors which joined recently: |
| 221 | + |
| 222 | +- **Bruce agency ($250)** – Investing in JAMstack, headless and touchless experiences since 2007, with over 250+ projects built. <https://bruce.agency/> |
| 223 | +- **Robert Lowe ($200)** – freelancer with great experience in Realtime web, mobile and desktop apps <http://robertlowe.ca> |
| 224 | + |
| 225 | +And thanks a lot to regular backers – [ScrapeHero](https://www.scrapehero.com/marketplace/) $5,[Woorke](https://woorke.com) $2, [420 Coupon Codes](https://420couponcodes.com/) $2,[ScrapingBee](https://www.scrapingbee.com/) $2, [Adapt.js](https://adaptjs.org/) $2. |
| 226 | + |
| 227 | +Your donations inspire me to improve `graphql-compose` packages. And allow to spend more time on it. Thank you very much for your support! |
| 228 | + |
| 229 | +You may consider to join with sponsoring `graphql-compose` and all its plugins via OpenCollective – https://opencollective.com/graphql-compose |
0 commit comments