Open nodkz opened 8 years ago
@nodkz I will write a detailed proposal, but basically the Graffiti model would be the connection between the adapters. The general adapter would have a Graffiti model as an input and a query object that has all the getOneResolver
, so on functions.. It is then passed down to every resolve
function on info.rootValue
.
Hmm, may be very interesting. Need code example.
Found this comment https://github.com/RisingStack/graffiti-mongoose/issues/64#issuecomment-158926841
I'd like this idea, and suggest implement it like wrappers:
let Post = mongoose.model('Post');
// current behaviour
const schema = getSchema([Post]);
// add wrapper
const schema = getSchema([withFilesConnection(Post)]);
// add more
const schema = getSchema([withFriendsQueries(withFilesConnection(Post))]);
Reusable wrappers under mongoose models is simple and extendable solution.
For example virtualFieldsWrapper
can solve task of populating a mongoose virtual fields. Mongoose has not define types for virtual fields. So wrapper can solve this problem:
const Post = mongoose.model('Post');
const PostWithVirtual = withVirtualFields(Post, {'name.full': string, 'metaInfo': MetaSchema});
const schema = getSchema([PostWithVirtual]);
Also some hooks may be hidden under wrappers. So wrappers can eliminate extending graffiti options in mongoose schemas.
Proposal implementation of wrappers: https://github.com/nodkz/graffiti-mongoose/commit/0f72a8b2714312197d3bb149373ecc5e8904faef
import mongoose, { Schema } from 'mongoose';
import { GraphQLString } from 'graphql/type';
import ExtraFieldsWrapper from 'lib/graffiti-mongoose/src/wrappers/extra-fields';
import VirtualFieldsWrapper from 'lib/graffiti-mongoose/src/wrappers/virtual-fields';
const CvSchema = new Schema(
{
name: {
type: String,
description: 'Person name',
},
},
{
toObject: { virtuals: true }, // for population virtual values in resolver
}
);
CvSchema.virtual('name333').get(function () {
return '!!!!!!!!' + this._id + '!!!!!!!!';
});
const Cv = mongoose.model('Cv', CvSchema);
let CvWrapped = new ExtraFieldsWrapper(Cv, {
extraFields: {
_session: {
type: GraphQLString,
resolve(rootValue, args, info) {
return `This is extra field which returns id: ${rootValue._id}`;
},
},
}
});
CvWrapped = new VirtualFieldsWrapper(CvWrapped, {
virtualFields: {
name333: 'String',
}
});
const Schema = getSchema([CvWrapped]);
New problem reveal today. I have Cv
model, and users can leave Notes
to it. But Notes
must be available only to those users who have left it.
Cv
does not store references to Notes
(only Note
know about user and cvId). So I want get such query:
query {
Cv(id: "1") {
onlyMyNotes: notes {
text
createdAt
}
}
}
To solve this problem I should:
Notes
, before I call getSchema
Cv
via my wrapper above ExtraFieldsWrapper
and resolve it with filtering by current user from req
. At this point I also want use DataLoader
for reducing number of queries to DB.So graffiti
does not allow to create gq-types, and use it later for extending other types. After that I may pass needed types to getSchema
for root query population. Also I want pass Notes
type to mutations, but not for queries.
Right now I will do monkeypatching :wink: of graffity
in my wrapper
-branch. I think it can give you some new thoughts about splitting the project in right way :stuck_out_tongue_winking_eye:
@nodkz Thanks for the very detailed proposal! I really appreciate it! :) I will dig into it more as soon as I have some free time.
Also I will use DataLoader (nice 30 min walkthrough from Lee Byron) between Mongoose and GraphQL. So need ability to switch getOneResolver
(use or not) this great feature, which can speed up querying for some graphs.
Eg. if we want load authors metadata for 100 articles, best choice would be using batchLoading
via DataLoader (internally it creates queue of promises and for next process tick combines all needed authors ids and make one batch query to retrieve authors data. After that all promises would be resolved in GQL). Awesome speed up!
App schema example https://gist.github.com/nodkz/164f410c78d7a8f01a0d
Extending Cv
model by field userData
in graphQL manner. This field type obtained from another mongoose model UserData
, which does not use mongoose references/population with Cv
. Also used custom resolve
function, which can use DataLoader
, but in this example use filtering by userId
.
For React/Relay components I should have ability use GraphQL Interfaces. Some components can be rendered under different types (so it can be done, if we add interface to such types and will use fragment on interfaceName), so to have such ability I create `ExtraInterfacesWrapper.
Commit: https://github.com/nodkz/graffiti-mongoose/commit/08aa4a394c8b53a442ec05929f1b0035418a1ae0 Updated gist with app/schema.js: https://gist.github.com/nodkz/164f410c78d7a8f01a0d
@nodkz Thanks for your effort! Sorry for not being active lately, I will try to keep up with your progress asap.
@nodkz any progress on this?
@sibelius internally I totally rewrite module with different architecture for our project purposes. But it not ready for OSS yet, cause API is not stable (I don't like that some things can be done via different ways and also I have not enough free time to document it).
@sibelius @tothandras just started develop new module https://github.com/nodkz/graphql-compose
Also you may see my previous version https://github.com/nodkz/graphql-mongoose which I won't publish to npm (reason - mash code in app).
@tothandras said: I have started to split the project into a general adapter (that waits a graffiti model as input) and a mongoose specific one.
Did you share somewhere a Proposal of your changes? How it will look like?
It will be great if new version will support:
getOneResolver
can be wrapped by DataLoader for performance reasons