Code Monkey home page Code Monkey logo

Comments (30)

aweary avatar aweary commented on July 22, 2024 1

Well, when it comes to automatically syncing the actual relationships between the sequelize models and the graphql types, yeah.

But I think connections can be used to query fields that aren't necessarily associations so that pagination information is provided.

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

See discussion here: #30
Root level node connections are not supported AFAIK, you need to implement an explicit viewer field on root and then connections under that.

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

@aweary Knows more about this than me, hopefully he can shed some light on a practical implementation.

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

@bsr203 Are you on the absolute latest version? We fixed something in regards to this and published a new version last night.

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

HI @mickhansen

thanks for the response. I just updated to the latest release and didn't solve it. You are right that I should not be using connection at root level. I have done it wrong above, but I was doing it correct in my app. see the schema below. so I have the viewer node where all the types are attached to as fields.

my viewer looks like this

'use strict';

import { GraphQLObjectType } from 'graphql';
import { globalIdField } from 'graphql-relay';
import * as queryFields from '../rootQueryFields';

function attachFields(refs, fields) {
  return Object.keys(fields)
    .reduce((acc, key) => {
      if (typeof fields[key] === 'function') {
        acc[key] = fields[key](refs);
      }
      return acc;
    }, {});
}

export default (refs) => new GraphQLObjectType({
  name: 'Viewer',
  fields: () => ({
    id: globalIdField('Viewer'),
    ...attachFields(refs, queryFields),
  }),
  interfaces: [refs.nodeInterface],
});

attachFields adds all the queries to viewer. you can see this pattern here

please let me know anything has to be done at the viewer definition so that graphql-sequelize knows the connection. I checked the source here https://github.com/mickhansen/graphql-sequelize/blob/master/src/resolver.js#L28

and as far I see it doesn't see my definition of viewer field as an association. May be I need to define it in sequelize schema, which I don't do anything for viewer. I don't want viewer in the database so I didn't define it. The connections under users work. so, may be I need to define a model for viewer but need a way to not treat it as a database entity. Am I on the right direction? How should my schema for viewer looks like ?

thanks again for the package and helping me out. Cheers.

screen shot 2015-10-08 at 6 35 40 am

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

It doesn't look like you are using the relay helpers from graphql-sequelize?
Please take a look at the relay tests: https://github.com/mickhansen/graphql-sequelize/blob/master/test/relay.test.js

Specifically https://github.com/mickhansen/graphql-sequelize/blob/master/test/relay.test.js#L158 which shows how to use the sequelize node type mapper with custom types ('viewer' => viewerType).

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

I was using type mapper, but wasn't using it for viewer. I added that now.

nodeTypeMapper.mapTypes({
  [User.name]: refs.user,
  'Viewer': refs.viewer
});

but the result is same. For this query from graphiql

{
  viewer {
        users {
            edges {node {email}}
          }
        }
}

I see this log in my terminal

POST http://localhost:3002/graphql 200 142.504 ms - 33802
Executing (default): SELECT `id`, `email` FROM `users` AS `User` LIMIT 1;

and this is the response

{
  "data": {
    "viewer": {
      "users": {
        "edges": null
      }
    }
  }
}

so it is a request for single node.

In the test you pointed out, I am not sure the schema reflect my situation.

The discussion about how to define viewer field and the reason is here . facebook/relay#112

so, in the test schema, there should only one field viewer of type viewerType and viewerType will have query fields like user, users,.. which could be list, connection etc. Do you think so too, then we can try it out ?

In other words, how I can I make

users: {
            type: new GraphQLList(userType),
            args: {

https://github.com/mickhansen/graphql-sequelize/blob/master/test/relay.test.js#L183
to

users: {
           type: userConnection.connectionType,
            args: {

since it is a list, I can't paginate. so, I guess it needs to be a connection field of viewer

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

Take a look at https://github.com/mickhansen/graphql-sequelize/blob/master/test/relay.test.js#L115

Can you post your entire setup in a gist so we can take a look? But i don't really have any practical experience with relay yet so we need to see what @aweary says.

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

thanks.. I will get a repo in an hour.

from graphql-sequelize.

aweary avatar aweary commented on July 22, 2024

On my way to the office, will review once I get there!

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

Hi @mickhansen

you can see the changes here https://github.com/bsr203/graphql-sequelize/branches

sorry, I am not an expert user of github so my changes are in two branches.

1 . test-connection-viewer

In this the query users is on root and we agree this doesn't work with relay at the moment.

2 . test-connection-viewer-2

In this, I tried to add a query field users to viewer. In my view, viewer is the current user (could be anonymous or a logged in). I need paginated (connection) queries on viewer which may allow access to all the models within the system. say all/paginated User, Task etc.

so, we may have to define an association for Viewer.

sequelize.define('Viewer' ... 
    Viewer.Users = Viewer.hasMany(User, {as: 'users'});

I tried it though, but didn't work. Also we probably don't need the viewer in database?

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

If you want it to work magically, viewer should return a sequelize instance that has associations.
viewer is not a sequelize object you'll have to do more work and play around with it a little bit (you probably can't use sequelize resolvers as is, likely have to wrap them).

This project is still experimental so you'll have to play around with it and debug the code as you code along :)

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

thanks. Yup, I may have to make viewer a sequelize instance with all the associations and not having it in the database. Will explore it. Hope @aweary will have some pointers. Thanks again.

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

What is your viewer? In my app i just return models.User.build({id: jwt.userId}, {isNewRecord: false})

from graphql-sequelize.

aweary avatar aweary commented on July 22, 2024

You shouldn't have to make a model for viewer, that was the point of #31. I'll have to look at the resolver function and whether the isConnection and handleConnection helpers are correctly identifying/handling a connection from a pure GraphQL type.

We've personally moved towards using a userType for our viewer which sidesteps this, but we should still be able to to resolve these connections.

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

We've personally moved towards using a userType for our viewer which sidesteps this, but we should still be able to to resolve these connections.

does that mean userType will have associations to all the models in the system . say I want to list all the users in the system, so will user type has a query field users ? How do we handle anonymous user, which may not have a user instance?

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

resolver works with the source, if possible, so if you want magic association handles it needs to be an instance, otherwise you can plug into the resolvers to work with your custom viewer type.

from graphql-sequelize.

aweary avatar aweary commented on July 22, 2024

does that mean userType will have associations to all the models in the system . say I want to list all the users in the system, so will user type has a query field users ? How do we handle anonymous user, which may not have a user instance?

Yes, the user is considered the viewer and will have fields for all the other types it is allowed to query. We use the rootValue to pass in our authentication information and if no information is provided we resolve a null user who represents anonymous users.

Anyways, I'm looking at the resolve function now, I'll see what I can do.

if (association && source.get(association.as) !== undefined) {
      if (isConnection(info.returnType)) {
        return handleConnection(source[info.fieldName], args);
      }
      return source.get(association.as);
    }

At a glance it looks like it will only check for connections if its passed an association in the resolver function.

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

@aweary connections really only makes sense as an association does it not? Otherwise it's not really a connection.

Atleast from the point of view of automatic handling.

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

@aweary Definitely, and i suppose when relay will support root connections Sequelize should aswell, i was simply thinking in the context of a viewer.

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

In case of use with relay, we may need a virtual model like Viewer which is not in DB but has associations to all the model (so we may need to define the model and associations Viewer.Users = Viewer.hasMany(User, {as: 'users'});, ..). So, when a query arrives for the root node and association, it gives a sequelize instance and carry on with the associations. So, I have to think what to return for viewer

What is your viewer? In my app i just return models.User.build({id: jwt.userId}, {isNewRecord: false})

@mickhansen you asked about it. I was doing

      resolve: (root) => root,

Viewer not really an instance of User as I may store some info specific to the viewer (logged in user / anonymous)

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

@bsr203 Well not sure how you expect graphql-sequelize to handle it. If the connections need to be scoped to the viewer then we need to know the relationship.

The library could attempt to query for anonymous viewers but i think that should be explicit (security/leak concerns if implicit imo).

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

In my case, sequelize resolver is only called once the authorization check is done. the middleware checks the token and all the resolve and root query node is wrapped in authorization check.

from graphql-sequelize.

aweary avatar aweary commented on July 22, 2024

I'm not entirely sure how we'd support this. Currently the process for resolving a connection looks like

  resolver = function (source, args, info) {
    if (association && source.get(association.as) !== undefined) {
      if (isConnection(info.returnType)) {
        return handleConnection(source[info.fieldName], args);
      }
      return source.get(association.as);
    }

It returns source[info.fieldName] to handleConnection. So for the users connection it would pass source['users'] which is passed to connectionFromArray which iterates through the items and returns pagination information.

The problem is that the source for viewer is resolved to:

{ name: 'Viewer!', id: 1 }

Which, of course, has no users information to pass to connectionFromArray. We need to change how it resolves the viewer or resolve the users list beforehand somewhere and have it available in the source.

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

Ya, I am fine defining a model for Viewer (not not to persist)

Viewer = sequelize.define('Viewer', {
      name: {
        type: Sequelize.STRING
      }
    }, {
      timestamps: false
    });
    Viewer.Users = Viewer.hasMany(User, {as: 'users'});

...
schema = new GraphQLSchema({
      query: new GraphQLObjectType({
        name: 'RootQueryType',
        fields: {
          viewer: {
            type: viewerType,
            //resolve: (root) => (root)
            resolve: () => Viewer.build()

          },

It didn't work for me, and still digging in.

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

It is working

    Viewer = sequelize.define('Viewer', {
      name: {
        type: Sequelize.STRING
      }
    }, {
      timestamps: false
    });
    Viewer.Users = Viewer.hasMany(User, {as: 'users'});

 viewerType = new GraphQLObjectType({
      name: 'Viewer',
      description: 'root viewer for queries',

      fields: () => ({
        id: globalIdField('Viewer'),
        name: {
          type: GraphQLString,
          resolve: () => 'Viewer!'
        },
        users: {
          type: userConnection.connectionType,
          args: connectionArgs,
          resolve: resolver(Viewer.Users)
        }
      }),
      interfaces: [nodeInterface]
  });


it.only('should resolve an array of objects containing connections', function () {
    var users = this.users;

    return graphql(schema, `
      {
        viewer {
          users {
            edges {
              node {
                name
              }
            }
          }
        }
      }
    `).then(function (result) {
      if (result.errors) throw new Error(result.errors[0].stack);

      expect(result.data.viewer.users.edges.length).to.equal(users.length);
      result.data.viewer.users.edges.forEach(function (edge, k) {
        expect(edge.node.name).to.equal(users[k].name);
        //expect(edge.node.tasks.edges).to.have.length.above(0);
      });

    });
  });

I guess I did resolve: resolver(Viewer.User) instead of resolve: resolver(Viewer.Users) earlier I tried the same by defining model for Viewer.

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

Hi,

A quick question

If I define the Viewer model as below, it works. but, If I do with classMethods -> associate it doesn't work. It is like defining the association through classMethods adds a foreign key, but not in the below case. That can't be true but can't explain why it work in one and not in the other case. Any help appreciated. thanks.

Viewer = sequelize.define('Viewer', {
      name: {
        type: Sequelize.STRING
      }
    }, {
      // classMethods: {
      //   associate: function (models) {
      //     Viewer.Users = Viewer.hasMany(models.User, { as: 'users' });
      //   }
      // },
      timestamps: false
    });

    Viewer.Users = Viewer.hasMany(User, {as: 'users'});

update:

uses the same code as here to build the association dynamically
http://sequelize.readthedocs.org/en/1.7.0/articles/express/

Object.keys(db).forEach(function(modelName) {
  if ("associate" in db[modelName]) {
    db[modelName].associate(db);
  }
});

@aweary when you use userType for viewer, does it create a foreign key UserId in all the associations. then what would you set for that field, as if there are many users in the system it can't bound to a single user id !!!

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

@bsr203 make sure the associate method is actually called, it's likely not at that point in time.
@bsr203 i suggest you set your resolvers to not use associations and just do regular finds if that is your intention, i have no idea what you're actually looking to do here (ignore viewer?)

from graphql-sequelize.

bsr203 avatar bsr203 commented on July 22, 2024

thanks @mickhansen. I will go with your suggestion than expecting the magic :-)

the viewer is just a workaround for relay doesn't support connection at root level. Also, I may use VIewer to hold any information useful to the client if the viewer is authenticated.

Using this library, I was expecting to make use of pagination support, but I will look at how it's done.

from graphql-sequelize.

mickhansen avatar mickhansen commented on July 22, 2024

Pagination support is not fully implemented yet, currently relay will fetch all records and then do processing.
It's on our list to improve that of course :)
But graphql, relay and the combination here with Sequelize are all highly new and experimental so expect to get your hands dirty :)

from graphql-sequelize.

Related Issues (20)

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.