Git Product home page Git Product logo

graffiti-mongoose's Introduction

⚠ Notice: the development of the package is discontinued. Use it for educational purposes and hobby projects only.

graffiti Mongoose

npm version CircleCI bitHound Overall Score Known Vulnerabilities

Mongoose (MongoDB) adapter for GraphQL.

graffiti-mongoose generates GraphQL types and schemas from your existing mongoose models, that's how simple it is. The generated schema is compatible with Relay.

For quick jump check out the Usage section.

Install

npm install graphql @risingstack/graffiti-mongoose  --save

Example

Check out the /example folder.

cd graffiti-mongoose
npm install # install dependencies in the main folder
cd example
npm install # install dependencies in the example folder
npm start # run the example application and open your browser: http://localhost:8080

Usage

This adapter is written in ES6 and ES7 with Babel but it's published as transpiled ES5 JavaScript code to npm, which means you don't need ES7 support in your application to run it.

Example queries can be found in the example folder.

usual mongoose model(s)
import mongoose from 'mongoose';

const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    // field description
    description: 'the full name of the user'
  },
  hiddenField: {
    type: Date,
    default: Date.now,
    // the field is hidden, not available in GraphQL
    hidden: true
  },
  age: {
    type: Number,
    indexed: true
  },
  friends: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  }]
});

const User = mongoose.model('User', UserSchema);
export default User;
graffiti-mongoose
import {getSchema} from '@risingstack/graffiti-mongoose';
import graphql from 'graphql';
import User from './User';

const options = {
  mutation: false, // mutation fields can be disabled
  allowMongoIDMutation: false // mutation of mongo _id can be enabled
};
const schema = getSchema([User], options);

const query = `{
    users(age: 28) {
      name
      friends(first: 2) {
        edges {
          cursor
          node {
            name
            age
          }
        }
        pageInfo {
          startCursor
          endCursor
          hasPreviousPage
          hasNextPage
        }
      }
    }
  }`;

graphql(schema, query)
  .then((result) => {
    console.log(result);
  });

Supported mongoose types

  • Number
  • String
  • Boolean
  • Date
  • [Number]
  • [String]
  • [Boolean]
  • [Date]
  • ObjectId with ref (reference to other document, populate)

Supported query types

  • query
    • singular: for example user
    • plural: for example users
    • node: takes a single argument, a unique !ID, and returns a Node
    • viewer: singular and plural queries as fields

Supported query arguments

  • indexed fields
  • "id" on singular type
  • array of "id"s on plural type

Which means, you are able to filter like below, if the age is indexed in your mongoose model:

users(age: 19) {}
user(id: "mongoId1") {}
user(id: "relayId") {}
users(id: ["mongoId", "mongoId2"]) {}
users(id: ["relayId1", "relayId2"]) {}

Supported mutation types

  • mutation
    • addX: for example addUser
    • updateX: for example updateUser
    • deleteX: for example deleteUser

Supported mutation arguments

  • scalar types
  • arrays
  • references

Examples:

mutation addX {
  addUser(input: {name: "X", age: 11, clientMutationId: "1"}) {
    changedUserEdge {
      node {
        id
        name
      }
    }
  }
}
mutation updateX {
  updateUser(input: {id: "id=", age: 10, clientMutationId: "2"}) {
    changedUser {
      id
      name
      age
    }
  }
}
mutation deleteX {
  deleteUser(input: {id: "id=", clientMutationId: "3"}) {
    ok
  }
}

Resolve hooks

You can specify pre- and post-resolve hooks on fields in order to manipulate arguments and data passed in to the database resolve function, and returned by the GraphQL resolve function.

You can add hooks to type fields and query fields (singular & plural queries, mutations) too. By passing arguments to the next function, you can modify the parameters of the next hook or the return value of the resolve function.

Examples:

  • Query, mutation hooks (viewer, singular, plural, mutation)
const hooks = {
  viewer: {
    pre: (next, root, args, request) => {
      // authorize the logged in user based on the request
      authorize(request);
      next();
    },
    post: (next, value) => {
      console.log(value);
      next();
    }
  },
  // singular: {
  //   pre: (next, root, args, context) => next(),
  //   post: (next, value, args, context) => next()
  // },
  // plural: {
  //   pre: (next, root, args, context) => next(),
  //   post: (next, value, args, context) => next()
  // },
  // mutation: {
  //   pre: (next, args, context) => next(),
  //   post: (next, value, args, context) => next()
  // }
};
const schema = getSchema([User], {hooks});
  • Field hooks
const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    hooks: {
      pre: (next, root, args, request) => {
        // authorize the logged in user based on the request
        // throws error if the user has no right to request the user names
        authorize(request);
        next();
      },
      // manipulate response
      post: [
        (next, name) => next(`${name} first hook`),
        (next, name) => next(`${name} & second hook`)
      ]
    }
  }
});
query UsersQuery {
  viewer {
    users(first: 1) {
      edges {
        node {
          name
        }
      }
    }
  }
}
{
  "data": {
    "viewer": {
      "users": {
        "edges": [
          {
            "node": {
              "name": "User0 first hook & second hook"
            }
          }
        ]
      }
    }
  }
}

Test

npm test

Contributing

Please read the CONTRIBUTING.md file.

License

MIT

graffiti-mongoose's People

Contributors

bruno12mota avatar dangreenisrael avatar gcoupelant avatar gergelyke avatar greenkeeperio-bot avatar gyzerok avatar hardchor avatar hekike avatar jashmenn avatar nodkz avatar peteyycz avatar sibelius avatar tothandras avatar ubcent avatar yoadsn avatar zopf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graffiti-mongoose's Issues

not require the use of babel-node in npm package

I am already using babel-node programmatically in my project. But babel-node doesn't go through node_modules. If I instruct it to do so, then it fails on an express middleware ("static is a reserved keyword").
It would make it easier to not require the use of babel in the npm package.

custom resolve for a field

Hi,

I would like to know if there's a way (or if you're envisioning one) of having a custom resolve method for a specific field. For instance, imagine I've got a field that to be populated I must engage with a third party API or another database.

Best regards.

Mongoose Objects returning invalid json

Not sure if this is irrelevant b/c future plans to support mongoose objects #4.

But currently the string type returned is invalid json. It appears the json returned has single quotes instead of doubles.

This (invalid JSON)

{"name':'search_term','description':'Company Name: Whole Foods','_id':'564d1f91f5e4c16a32d69376'}"

Compared to this (valid):

"{"name":"search_term","description":"Company Name: Whole Foods","_id":"564d1f91f5e4c16a32d69376"}"

If you point me to the right file, I can make a PR. Also, maybe I'm being silly, but wouldn't #4. be solved just by throwing this string into a JSON.parse() function?

I can do that as well if you would like.

Best,
Jon

Support permission for queries and mutations

I am begginner with mongoose and graphql, and want ask to provide an example how add additional logic with graffiti-mongoose. For example:

{
  me {
    name
    age
    friends {
      name
    }
  }
}
  1. How I can realize me schema? According to the authorized user data in session I want obtain a current user profile.
  2. And how I can add filter only for active friends according to the session data? (Usual user can see only active friends, admin can see any type of friends.) For security reasons I can't allow to the client side do such query friends(active: true), it must be realized on the server side.

Thanks.

Cannot query field "fieldName" on "addSomeCollectionNamePayload"

Hi, Thanks for this fantastic module.

However, when I do according to the example, I get an error. And I don't know why it errors.

The schema

  const languageSchema = mongoose.Schema({
    name: {
      type: String,
      validate: {
        validator: function(v) {
          return _.isString(v) && v.length;
        },
        message: 'Language name should be present.'
      }
    }
  });

The query

mutation addLanguageJapanese {
    addLanguage(input: {clientMutationId: "anyString", name: "Japanese"}) {
        name, id
    }
}

The response

{
  "errors": [
    {
      "message": "Cannot query field \"name\" on \"addLanguagePayload\"."
    },
    {
      "message": "Cannot query field \"id\" on \"addLanguagePayload\"."
    }
  ]
}

[possible bug] updating array of strings

having described in my model images: [String] when i attempt to save

images: [
  "fc6092ab09574edd9cb82c665f97fb0b"
  "c01a8ab7196e45c6981cb58f58152db6"
  "3bcd62558a8a444ea9f1996efda062de"
  "a23530eb3694408f8e8ef3a81fe3ceed"
  "77dd890281ea4cd8b2cb4487c433dcdf"
]

in the database seems to save

"images": [
        "e\u007f{}=\u001b",
        "wM\\o\u001f\u001fgMyYVz",
        "3bcd62558a8a444ea9f1996efda062de",
        "a23530eb3694408f8e8ef3a81fe3ceed",
        "77dd890281ea4cd8b2cb4487c433dcdf"
    ]

This doesn't seems to be consistently reproduceable but seems to happen only when the value of the string contains hashes (perhaps they get confused at times with ObjectId?)

orderBy argument has no effect

The two orderBy tests in e2e.spec.js dont fail, if you switch NAME_DESC to NAME_ASC (and vice versa).

I noticed, that the actual Collection.find call in query.js gets called with

{
  sort: 'NAME_DESC'
}

instead of

{
  sort: { name: -1 }
}

It seems like the order keys somehow don't get resolved into the corresponding mongoose option.
If you can give me a bump in the right direction, I'd be happy to fix this.

Help needed: support null values of referenced fields

Need help with null values when using reference fields.

I have a NewsFeed schema that contains a Product, or a Post, or a Video. Defined as follows:

NewsFeed = new mongoose.Schema({
  date: { type: Date, required: true, default: Date.now },
  product: { type: ObjectId, ref: 'Product', required: false},
  post: { type: ObjectId, ref: 'Post', required: false },
  video: { type: ObjectId, ref: 'Post', required: false }, 
});

This means that aNewsFeed document contains a date and one element (one of product, post or video).

Ideally I what to use the GraphQL query bellow and fetch my newsfeed ordered by date and containing the applicable newsfeed element with the other elements absent or null.

query{
  viewer{
    homenewsfeeds(first:5,orderBy:DATE_DESC){
      count,
      edges{
        node{
          date,
          product {
            id,
            name
          },
          post{
            id,
            title,
            author {
              id,
              name
            }
          }
          video{
            id,
            title,
            duration,
          }
        }
      }
    }
  }
}

However I get the following error:

"statusCode": 400,
  "error": "Bad Request",
  "message": "Cannot read property 'toString' of null\nCannot read property 'toString' of null\nCannot read property 'toString' of null\nCannot read property 'toString' of null\nCannot read property 'toString' of null" 

My understating is that GraphQl is trying to load the referenced object even if is not in the current element.

I've already attempted to re-create the newsfeed forcing the elements to be required defaulting to null, with no success. I'm getting the same error

NewsFeed = new mongoose.Schema({
  date: { type: Date, required: true, default: Date.now },
  product: { type: ObjectId, ref: 'Product', required: true, default: null},
  post: { type: ObjectId, ref: 'Post', required: true, default: null},
  video: { type: ObjectId, ref: 'Post', required: true, default: null }, 
});

Another option would be refactor the NewsFeed as described bellow. However this will defeat the purpose of simpler GraphQL queries.

NewsFeed = new mongoose.Schema({
  date: { type: Date, required: true, default: Date.now },
  target: { type: ObjectId},
  type: { type: String, enum: ['Post','Product','Video']},
});

Comments? This is a limitation of GraphQL or current Mongoose-GraphQl implementation?

Getting selectionSet from fieldASTs with external fragment

How can I get selectionSet from fieldASTs with external fragment?

Consider following query

`
  query QueryWithFragment {
    todo(_id: "55a624bad009804e552eeea8") {
      ...TextFragment
    }
  }

  fragment TextFragment on Todo {
    text
  }
`

This query results in

{
  "kind": "Field",
  "alias": null,
  "name": {
    "kind": "Name",
    "value": "todo",
    "loc": {
      "start": 27,
      "end": 31,
      "source": {
        "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
        "name": "GraphQL request"
      }
    }
  },
  "arguments": [{
    "kind": "Argument",
    "name": {
      "kind": "Name",
      "value": "_id",
      "loc": {
        "start": 32,
        "end": 35,
        "source": {
          "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
          "name": "GraphQL request"
        }
      }
    },
    "value": {
      "kind": "StringValue",
      "value": "55a624bad009804e552eeea8",
      "loc": {
        "start": 37,
        "end": 63,
        "source": {
          "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
          "name": "GraphQL request"
        }
      }
    },
    "loc": {
      "start": 32,
      "end": 63,
      "source": {
        "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
        "name": "GraphQL request"
      }
    }
  }],
  "directives": [],
  "selectionSet": {
    "kind": "SelectionSet",
    "selections": [{
      "kind": "FragmentSpread",
      "name": {
        "kind": "Name",
        "value": "TextFragment",
        "loc": {
          "start": 76,
          "end": 88,
          "source": {
            "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
            "name": "GraphQL request"
          }
        }
      },
      "directives": [],
      "loc": {
        "start": 73,
        "end": 88,
        "source": {
          "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
          "name": "GraphQL request"
        }
      }
    }],
    "loc": {
      "start": 65,
      "end": 94,
      "source": {
        "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
        "name": "GraphQL request"
      }
    }
  },
  "loc": {
    "start": 27,
    "end": 94,
    "source": {
      "body": "\n  query UseFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ...TextFragment\n    }\n  }\n\n  fragment TextFragment on Todo {\n    text\n  }\n",
      "name": "GraphQL request"
    }
  }
}

Now lets take a look in InlineFragment version

`
  query QueryWithoutFragment {
    todo(_id: "55a624bad009804e552eeea8") {
      ... on Todo {
        text
      }
    }
  }
`

We can easily access requested fields for fragment in selectionSet

{
  "kind": "Field",
  "alias": null,
  "name": {
    "kind": "Name",
    "value": "todo",
    "loc": {
      "start": 36,
      "end": 40,
      "source": {
        "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
        "name": "GraphQL request"
      }
    }
  },
  "arguments": [{
    "kind": "Argument",
    "name": {
      "kind": "Name",
      "value": "_id",
      "loc": {
        "start": 41,
        "end": 44,
        "source": {
          "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
          "name": "GraphQL request"
        }
      }
    },
    "value": {
      "kind": "StringValue",
      "value": "55a624bad009804e552eeea8",
      "loc": {
        "start": 46,
        "end": 72,
        "source": {
          "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
          "name": "GraphQL request"
        }
      }
    },
    "loc": {
      "start": 41,
      "end": 72,
      "source": {
        "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
        "name": "GraphQL request"
      }
    }
  }],
  "directives": [],
  "selectionSet": {
    "kind": "SelectionSet",
    "selections": [{
      "kind": "InlineFragment",
      "typeCondition": {
        "kind": "Name",
        "value": "Todo",
        "loc": {
          "start": 89,
          "end": 93,
          "source": {
            "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
            "name": "GraphQL request"
          }
        }
      },
      "directives": [],
      "selectionSet": {
        "kind": "SelectionSet",
        "selections": [{
          "kind": "Field",
          "alias": null,
          "name": {
            "kind": "Name",
            "value": "text",
            "loc": {
              "start": 104,
              "end": 108,
              "source": {
                "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
                "name": "GraphQL request"
              }
            }
          },
          "arguments": [],
          "directives": [],
          "selectionSet": null,
          "loc": {
            "start": 104,
            "end": 108,
            "source": {
              "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
              "name": "GraphQL request"
            }
          }
        }],
        "loc": {
          "start": 94,
          "end": 116,
          "source": {
            "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
            "name": "GraphQL request"
          }
        }
      },
      "loc": {
        "start": 82,
        "end": 116,
        "source": {
          "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
          "name": "GraphQL request"
        }
      }
    }],
    "loc": {
      "start": 74,
      "end": 122,
      "source": {
        "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
        "name": "GraphQL request"
      }
    }
  },
  "loc": {
    "start": 36,
    "end": 122,
    "source": {
      "body": "\n  query QueryWithoutFragment {\n    todo(_id: \"55a624bad009804e552eeea8\") {\n      ... on Todo {\n        text\n      }\n    }\n  }\n",
      "name": "GraphQL request"
    }
  }
}

I'm currently working on conversion fieldASTs to MongoDB projections. I've done with InlineFragment. Any thoughts?

Error: _id.map is not a function

I get an error if I use after to fetch new items. Example query (below). Query is produced by Relay, but should be self explanatory.

query Product {
  node(id:"UHJvZHVjdDo1MGEwNDU1ZDBkNmNhYjA1MmFlN2VmZTk=") {
    ...F1
  }
}

fragment F0 on Attribute {
  id,
  name
}

fragment F1 on Product {
  _attributeslhN3L:attributes(after:"Y29ubmVjdGlvbi41NDVhYzUzY2JlYjcwODYzMWJjODczYTA=",first:2) {
    edges {
      cursor,
      node {
        ...F0,
        id
      }
    },
    pageInfo {
      hasNextPage,
      hasPreviousPage
    }
  },
  id
}

If I run the same query without after it will run successfully, even with a higher number of items

This is my (redacted) model structure:

ProductSchema = new mongoose.Schema({
  name: { type: String, required: true,
  },
  attributes: { type: [{ type: ObjectId, ref: 'Attribute' }], index: true },
})..
Attribute = new mongoose.Schema({
  name: {
    type: 'String',
    description: 'name of the attribute',
    maxLength: 60,
    required: true
  })..

I'm using:

"@risingstack/graffiti": "^3.0.1",
"@risingstack/graffiti-mongoose": "^5.0.5",
"express": "^4.13.4",
"mongoose": "^4.3.7",

Extending grafitti schema

getSchema returns a GraphQLSchema object based on MongooseModels, how do I go about adding my own types to this schema?

Consider the use case:
I have a Post object with getLikedFriends method, which looks up all Post.likes references and queries Users collection to check if any of the User.friends references for the loggedIn user match with Post.likes

So instead of creating a new Mongoose model and re-implementing the logic inside my pre-existing getLikedFriends method, I'd like to just add this as a new type to the GraphQL schema and let the resolve function call Post.getLikedFriends(ctx.loggedInUser)

This is something I can easily do using vanilla GraphQL but not sure how to augment the schema returned by graffiti :/

Support for Mongoose methods and statics

Is there a way to wire up the existing model methods and statics with resolve functions?

i.e. if my model User has a method User.statics.findSimilar() is there a way for me to expose this as a GraphQL type?

If this was a new application I would use pre and post hooks but theres TONNES of existing business logic within the mongoose statics that I would rather not replicate by hand!

Any thoughts?

Return better mongoose errors

For example I have model with required field:

import mongoose from 'mongoose';

const UserSchema = new mongoose.Schema({
    email: {
        type: String,
        unique: true,
        trim: true,
        required: 'Email is required'
    },

    createdAt: {
        type: Date,
        default: Date.now
    }
});

const User = mongoose.model('User', UserSchema);

export default User;

but when I send mutation to server like {"mutation { addUser(input: {clientMutationId: "1"}) { clientMutationId } }"} I receive not well enough descriptive error:

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "User validation failed"
}

Sorting by arbitrary field

Is there a way to specify the order of items returned by the server, e.g. { sort: { score: 1 } }?

As far as I know, Relay and GraphQL have no concept of sorting. I was wondering if graffiti-mongoose allows adding the sort parameter to the generated queries. Or maybe there is a way to build custom queries and attach them to auto-generated ones?

ReferenceError: regeneratorRuntime is not defined

Hi, I ran into this error

/Users/1/Developer/openv/node_modules/@risingstack/graffiti-mongoose/lib/query/query.js:351
  var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(graffitiModel, args, info) {
                              ^

ReferenceError: regeneratorRuntime is not defined

npm install error

hey, while running npm install i get the following error. do i have the wrong node version?

npm ERR! Linux 3.19.0-28-generic
npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "install"
npm ERR! node v5.2.0
npm ERR! npm  v3.3.12

npm ERR! Invalid name: "@risingstack/graffiti-mongoose"
npm ERR!
npm ERR! If you need help, you may report this error at:
npm ERR!     <https://github.com/npm/npm/issues>

npm ERR! Please include the following file with any support request:
npm ERR!     /home/mod-new/code/test/graffiti-mongoose/example/npm-debug.log

Inverse references

If I have two schemas like this:

var personSchema = Schema({
  name    : String,
  age     : Number,
});

var storySchema = Schema({
  _creator : { type: Schema.Types.ObjectId, ref: 'Person' },
  title    : String,
  fans     : [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});

It would be nice to be able to add a connection from person to stories that results in Story.find({_creator:...})

Road towards relay compatibility

N.B.: Moved this from RisingStack/graffiti#7

Just putting some thoughts down of currently missing features:

Unique Object IDs

Relay wants to be able to (re-)fetch any object in the graph by a unique ID via a root query named node:

node (id: "user123") {
  id
  name
}

Relay methods: fromGlobalId/toGlobalId
See: https://github.com/relayjs/relay-starter-kit/blob/master/data/schema.js#L115

Pagination

On the query side, relay requires first and after arguments (as well as last and before for backwards pagination):

users (first: 10, after: "user123") {
  edges {
    cursor
    node {
      id
      name
    }
  }
  pageInfo {
    hasNextPage
  }
}

Also see: https://facebook.github.io/relay/graphql/connections.htm

Mutation

TBC

Fragment support

Connections

I expect graphql-relay-js to help us out with the more intricate details

I hope we can work towards relay compatibility. Please let me know if this is more appropriate for an adapter, e.g. graffiti-mongoose

Graffiti - Running out of memory

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory

I'm constantly getting our of memory errors and my app crashes.

==== JS stack trace =========================================

Security context: 0x1991fcae3ac1
1: /* anonymous /(aka / anonymous /) [/usr/local/lib/node_modules/babel-cli/node_modules/core-js/modules/es6.promise.js:~67] [pc=0xc21e0f8675](this=0x1991fca04189 ,it=0x180c23cacaa9 <a Promise with map 0xcef15a14bc1)
2: /
anonymous /(aka / anonymous */) [/usr/local/lib/node_modules/babel-cli/node_modules/core-js/modules/es6.promise.js:107] [pc=0xc221293e68] (this=0x19...

Silent failure with graphiql hanging on body parse

I'm not exactly sure whats going here here, but I installed graffiti-mongoose on my own user schema

var mongoose = require('mongoose'),
  Schema = mongoose.Schema,
  passportLocalMongoose = require('passport-local-mongoose')
var crypto = require('crypto')

var User = new Schema({
    email: String,
    unique_token: String,
    angellist: {
        data: Schema.Types.Mixed,
        oauth_token: String,
        refresh_token: String
    },
    google: {
        data: Schema.Types.Mixed,
        oauth_token: String,
        refresh_token: String
    }
})

In my expressjs server file I have this.

app.use(graffiti.express({
    schema: getSchema([User])
}))

It appears that when I load graphql localhost:8080/graphql

graffiti is hanging on this line when the first introspection query is passed (38 in the published source):

  `return (0, _coBody.json)(request).then(function (body) {`

The query body is this:

{ query: '\n  query IntrospectionQuery {\n    __schema {\n      queryType { name }\n      mutationType { name }\n      types {\n        ...FullType\n      }\n      directives {\n        name\n        description\n        args {\n          ...InputValue\n        }\n        onOperation\n        onFragment\n        onField\n      }\n    }\n  }\n\n  fragment FullType on __Type {\n    kind\n    name\n    description\n    fields {\n      name\n      description\n      args {\n        ...InputValue\n      }\n      type {\n        ...TypeRef\n      }\n      isDeprecated\n      deprecationReason\n    }\n    inputFields {\n      ...InputValue\n    }\n    interfaces {\n      ...TypeRef\n    }\n    enumValues {\n      name\n      description\n      isDeprecated\n      deprecationReason\n    }\n    possibleTypes {\n      ...TypeRef\n    }\n  }\n\n  fragment InputValue on __InputValue {\n    name\n    description\n    type { ...TypeRef }\n    defaultValue\n  }\n\n  fragment TypeRef on __Type {\n    kind\n    name\n    ofType {\n      kind\n      name\n      ofType {\n        kind\n        name\n        ofType {\n          kind\n          name\n        }\n      }\n    }\n  }\n',
  variables: null }

Any idea of what's going on?

Thanks,
Jon

Edge ordering

Hi.

I was looking for a way to order edges based on the parent document array, but couldn't find a way. For example, given a model with Person and Destinations and a database:

const destinations = [
  new Destination({name: 'London'},
  new Destination({name: 'Paris'},
  new Destination({name: 'Berlin'}
];

const people = [
  new Person({name: 'John', trips: [destinations[2], destinations[0]]}),
  new Person({name: 'Jane', trips: [destinations[1]]}),
  new Person({name: 'Jill', trips: [destinations[1], destinations[0]]})
];

and a query:

{
  people {
    name
    trips {
      edges {
        node {
          name
        }
      }
    }
  }
}

I would expect the result:

{
  "data": {
    "people": [
      {
        "name": "John",
        "trips": {
          "edges": [
            {
              "node": {
                "name": "Berlin"
              }
            },
            {
              "node": {
                "name": "London"
              }
            }
          ]
        }
      },
      {
        "name": "Jane",
        "trips": {
          "edges": [
            {
              "node": {
                "name": "Paris"
              }
            }
          ]
        }
      },
      {
        "name": "Jill",
        "trips": {
          "edges": [
            {
              "node": {
                "name": "Paris"
              }
            },
            {
              "node": {
                "name": "London"
              }
            }
          ]
        }
      },
    ]
  }
}

Is that currently possible with graffiti? I couldn't find a way to do it, nor to order in other collections. Thank you in advance for the attention!

Confusing readme example

What does following code from readme example means? How can we return from a module?

return yield graphql(schema, query);

Adding field hooks to existing model

The field hook example shows creation of a new model using mongoose.schema, how do I add field hooks to existing models?

Eg. If I want to format the a timestamp into 'x hours ago' string. Please see pseudo-code for what I'm trying to achieve:

var User = mongoose.model('User')
User._created.hooks = {
  post:  [
    (next, date) => next(moment(date).fromNow())
  ]
}
var schema = getSchema([User])

I'm sure there's a way to do this, just struggling to get my head around hooks docs!

Thanks in advance :)

Mutation resolve hooks question

Hi!

You mentioned in the docs:

You can add hooks to type fields and query fields (singular & plural queries, mutations) too. By passing arguments to the next function, you can modify the parameters of the next hook or the return value of the resolve function."

Curious what is meant by "passing arguments to the next function..." -- how does one make use of this functionality?

One example of what I'm trying to do is get access to the original mutation context object (the third argument to the pre hook) in the post hook. The reason for that is I want to introspect on the mutation that occurred to determine any side effects, but I want to do that AFTER the mutation is complete not before (where I have access to this information) Is there a simple way to do this via the next callback? Can I effectively pass that "through" to the post hook?

Test failing: e2e

Hi, I've cloned the project and all the e2e singular query tests are failing. It's just happening to me or there's something wrong?

Add item to edge of node

I'm trying to add a Slide to a Deck's array of Slides but I can't seem to find a mutation that works. Below is my Mongoose schema.

const DeckSchema = new mongoose.Schema({
  name: {
    type: String,
    description: 'the full name of the deck'
  },
  slides: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Slide'
  }]
});

const SlideSchema = new mongoose.Schema({
  name: {
    type: String,
    description: 'the full name of the slide'
  },
  component: {
    type: String,
    description: 'name of slide component'
  }
});

Possiblity the docs are out of date?

Have tried running the demo, and this query, as described in the docs, from the local graphql server:

mutation addX {
  addUser(input: {name: "X", age: 11, clientMutationId: "1"}) {
    id
    name
  }
}

returns an error:

{
  "errors": [
    {
      "message": "Cannot query field \"id\" on \"addUserPayload\"."
    },
    {
      "message": "Cannot query field \"name\" on \"addUserPayload\"."
    }
  ]
}

while this query works fine:

mutation addX {
  addUser(input: {name: "X", age: 11, clientMutationId: "1"}) {
    changedUserEdge {
      node {
        id
        name
      }
    }
  }
}

So I wonder if the docs may be out of sync?

Cursors don't work

In query.getList:

selector._id = {
  $in: _id.map((id) => processId({id}))
};

_id is not an Array. It is actually { $gt: ... }

Cannot get example to work

No matter what I try as a query, I get the following error in devtools (react.min.js):

Uncaught TypeError: Cannot read property 'getQueryType' of undefined

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.