|
3 | 3 | <!-- TOC depthFrom:2 --> |
4 | 4 |
|
5 | 5 | - [What's new?](#whats-new) |
6 | | - - [🛑 resolver creation (factory)](#🛑-resolver-creation-factory) |
| 6 | + - [A new way for resolver creation (via factory)](#a-new-way-for-resolver-creation-via-factory) |
| 7 | + - [An full example using TypeScript with mongoose, graphql-compose-mongoose, and a new DataLoader resolver.](#an-full-example-using-typescript-with-mongoose-graphql-compose-mongoose-and-a-new-dataloader-resolver) |
| 8 | + - [Expose `resolverFactory` for advanced cases of resolver creation](#expose-resolverfactory-for-advanced-cases-of-resolver-creation) |
7 | 9 | - [Mutations got `error: ErrorInterface` field in theirs payload for better error handling](#mutations-got-error-errorinterface-field-in-theirs-payload-for-better-error-handling) |
8 | 10 | - [Added a new `ValidationError`](#added-a-new-validationerror) |
9 | | -- [Improvements](#improvements) |
| 11 | +- [Enhancements](#enhancements) |
10 | 12 | - [Now `_id` field can be of any type (Int, String, Object)](#now-_id-field-can-be-of-any-type-int-string-object) |
11 | 13 | - [Add nested fields support, new operators `regex`, `exists` for `filter._operators`](#add-nested-fields-support-new-operators-regex-exists-for-filter_operators) |
12 | 14 | - [Better alias support for nested embedded fields](#better-alias-support-for-nested-embedded-fields) |
|
27 | 29 |
|
28 | 30 | ## What's new? |
29 | 31 |
|
30 | | -### 🛑 resolver creation (factory) |
| 32 | +### A new way for resolver creation (via factory) |
31 | 33 |
|
32 | | -- Refactor resolver creation #263 |
33 | | -- Expose `mongooseResolvers` for manual creation of resolvers #274 |
34 | | -- 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 |
35 | | -- 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')`) |
36 | | -- Improve Typescript definitions for resolvers |
| 34 | +Before 9.0.0 `graphql-compose-mongoose` generates type & resolvers together on `composeWithMongoose` call. This approach have the following disadvantages: |
| 35 | +- no static analysis for resolver names (are you sure that `findById` exists?) |
| 36 | +- under the hood generated around 15 resolvers and many graphql types for them (do you really use all of these resolvers?) |
| 37 | +- no static analysis for available args when you are crating relations |
| 38 | +- quite awkward mechanics of creating new resolvers, cloning, or wrapping them (what if you need to change type before resolvers creation? what if I need several copies of one resolver with a different set of args?) |
| 39 | +- no `go-to` support in IDE via ctrl+click for opening resolver definition. |
| 40 | + |
| 41 | +To overcome all these problems was created a new `composeMongoose` method. And I strongly recommend to migrate your codebase from the old `composeWithMongoose` to a new one method for making your code more stable and faster: |
| 42 | + |
| 43 | +```diff |
| 44 | +- import { composeWithMongoose } from 'graphql-compose-mongoose'; |
| 45 | ++ import { composeMongoose } from 'graphql-compose-mongoose'; |
| 46 | + |
| 47 | +// generate GraphQL types & resolvers from mongoose model |
| 48 | +- const UserTC = composeWithMongoose(UserModel); |
| 49 | ++ const UserTC = composeMongoose(UserModel); |
| 50 | + |
| 51 | +// getting generated resolver by name `findById` |
| 52 | +- const findByIdResolver = UserTC.getResolver('findById'); |
| 53 | ++ const findByIdResolver = UserTC.mongooseResolvers.findById(); |
| 54 | +``` |
| 55 | + |
| 56 | +Under the hood new `composeMongoose` method on its call: |
| 57 | +- will create just two types `ObjectTypeComposer` & `InputTypeComposer` |
| 58 | +- will not generate resolvers automatically, but instead of this, it adds `mongooseResolvers` property with a factory for resolver creation on demand. |
| 59 | + |
| 60 | +It will provide the following advantages: |
| 61 | +- You may programmatically modify `TypeComposer` and only after that generate resolvers with their sub-types based on already customized fields in `TypeComposer`. |
| 62 | +- Safe memory and time on the bootstrap server phase, because it won't generate unused resolvers. |
| 63 | +- You may call one resolver generator several times with different configurations. |
| 64 | +- `mongooseResolvers` factory has improved typescript definitions. And you will get not only a list of available resolvers by names but also their customization options. Moreover, all generated Resolvers will know its `source: Model<IDoc>`. |
| 65 | +- According to its customization options for every resolver, you may control how your resolvers will be generated. Also, it unlocks the ability in future releases to add more options like `before` & `after` hooks, ACL, additional filters, and sorts params. |
| 66 | + |
| 67 | +[Issue #263](https://github.com/graphql-compose/graphql-compose-mongoose/issues/263) |
| 68 | + |
| 69 | +### An full example using TypeScript with mongoose, graphql-compose-mongoose, and a new DataLoader resolver. |
| 70 | + |
| 71 | +An example of TypeScript usage with a lot of type checks and autosuggestions: |
| 72 | + |
| 73 | +```ts |
| 74 | +import { SchemaComposer } from 'graphql-compose'; |
| 75 | +import { composeMongoose } from 'graphql-compose-mongoose'; |
| 76 | +import { mongoose } from 'mongoose'; |
| 77 | + |
| 78 | +// type for context which is provided by graphql-server |
| 79 | +interface TContext { |
| 80 | + req: Request; |
| 81 | +} |
| 82 | + |
| 83 | +// creating a schema with TContext |
| 84 | +const schemaComposer = new SchemaComposer<TContext>(); |
| 85 | + |
| 86 | +// describe mongoose schema for User |
| 87 | +const UserSchema = new mongoose.Schema({ |
| 88 | + name: { type: String, required: true }, |
| 89 | +}); |
| 90 | + |
| 91 | +// define interface for your mongoose model IUser |
| 92 | +interface IUser extends mongoose.Document { |
| 93 | + name: string; |
| 94 | +} |
| 95 | + |
| 96 | +// describe mongoose schema for Post |
| 97 | +const PostSchema = new mongoose.Schema({ |
| 98 | + title: { type: String, required: true }, |
| 99 | + authorId: { type: mongoose.Types.ObjectId }, |
| 100 | + reviewerIds: { type: [mongoose.Types.ObjectId] }, |
| 101 | +}); |
| 102 | + |
| 103 | +// define interface for your mongoose model IPost |
| 104 | +interface IPost extends mongoose.Document { |
| 105 | + title: string; |
| 106 | + authorId?: mongoose.Types.ObjectId; |
| 107 | + reviewerIds?: [mongoose.Types.ObjectId]; |
| 108 | +} |
| 109 | + |
| 110 | +// Create mongoose models with typescript definitions |
| 111 | +// Bored with describing fields two times for Schema and for Interface – |
| 112 | +// try typegoose: https://typegoose.github.io/typegoose/ |
| 113 | +const UserModel = mongoose.model<IUser>('User', UserSchema); |
| 114 | +const PostModel = mongoose.model<IPost>('Post', PostSchema); |
| 115 | + |
| 116 | +// create ObjectTypeComposers via the new method |
| 117 | +const UserTC = composeMongoose(UserModel); |
| 118 | +const PostTC = composeMongoose(PostModel); |
| 119 | + |
| 120 | +// make relations between Post <--> Author |
| 121 | +// via adding a new field `author` and using DataLoader |
| 122 | +PostTC.addRelation('author', { |
| 123 | + resolver: UserTC.mongooseResolvers.dataLoader({ lean: true }), |
| 124 | + // ^^^ ^^^ |
| 125 | + // autosuggestion for available resolvers and available options |
| 126 | + prepareArgs: { |
| 127 | + // here will be allowed only `_id` key, cause only one this arg is available in `dataLoader` resolver |
| 128 | + _id: (s) => s.authorId, |
| 129 | + // ^^^ source is typed to IPost interface |
| 130 | + }, |
| 131 | + projection: { authorId: true }, |
| 132 | +}); |
| 133 | + |
| 134 | +// Describe another relation via `dataLoaderMany` |
| 135 | +PostTC.addRelation('reviewers', { |
| 136 | + resolver: UserTC.mongooseResolvers.dataLoaderMany({ lean: true }), |
| 137 | + prepareArgs: { |
| 138 | + _ids: (s) => s.reviewerIds, |
| 139 | + }, |
| 140 | + projection: { reviewerIds: true }, |
| 141 | +}); |
| 142 | + |
| 143 | +// defining schema entrypoints for Query |
| 144 | +schemaComposer.Query.addFields({ |
| 145 | + post: PostTC.mongooseResolvers.findById(), |
| 146 | + posts: PostTC.mongooseResolvers.findMany(), |
| 147 | + user: UserTC.mongooseResolvers.findById(), |
| 148 | + users: UserTC.mongooseResolvers.findMany({ sort: false }), |
| 149 | +}); |
| 150 | + |
| 151 | +// defining schema entrypoints for Mutation |
| 152 | +schemaComposer.Query.addFields({ |
| 153 | + postCreate: PostTC.mongooseResolvers.createOne(), |
| 154 | + userCreate: UserTC.mongooseResolvers.createOne(), |
| 155 | +}); |
| 156 | + |
| 157 | +// generating GraphQL schema instance |
| 158 | +const schema = schemaComposer.buildSchema(); |
| 159 | +``` |
| 160 | + |
| 161 | +### Expose `resolverFactory` for advanced cases of resolver creation |
| 162 | + |
| 163 | +`graphql-compose-mongoose` exposes `resolverFactory` variable which contains all available resolver generators, eg. `resolverFactory.createOne(someMongooseModel, someTC, customizationOpts)`. This resolver generation way unlocks the following scenarios: |
| 164 | +- you have several mongoose models with different databases connection and one TypeComposer, so you can reuse one GraphQL type for using it with different MongoDBs. |
| 165 | +- you have one mongoose model and want to use different GraphQL types (for admins with a full set of fields, and for clients with a reduced set of fields). |
| 166 | + |
| 167 | +The following example demonstrates how to use the second scenario with `ClonedPostTC` type for the reduced set of fields and existing mongoose `PostModel` for making requests to DB: |
| 168 | + |
| 169 | +```ts |
| 170 | +import { resolverFactory } from 'graphql-compose-mongoose'; |
| 171 | +import { PostModel, PostTC } from './the-example-above'; |
| 172 | + |
| 173 | +const ClonedPostTC = PostTC.clone('ReducedPost'); |
| 174 | +ClonedPostTC.getInputTypeComposer().removeField('authorId'); |
| 175 | + |
| 176 | +const createPostWithoutAuthor = resolverFactory.createOne(PostModel, ClonedPostTC, { suffix: 'CustomCreate' }); |
| 177 | +``` |
| 178 | + |
| 179 | +[Issue #274](https://github.com/graphql-compose/graphql-compose-mongoose/issues/274) |
37 | 180 |
|
38 | 181 | ### Mutations got `error: ErrorInterface` field in theirs payload for better error handling |
39 | 182 |
|
@@ -228,7 +371,7 @@ You will receive the following response: |
228 | 371 |
|
229 | 372 | [Issue #248](https://github.com/graphql-compose/graphql-compose-mongoose/issues/248) |
230 | 373 |
|
231 | | -## Improvements |
| 374 | +## Enhancements |
232 | 375 |
|
233 | 376 | ### Now `_id` field can be of any type (Int, String, Object) |
234 | 377 |
|
|
0 commit comments