Ошибка подписки GraphQL Server

Я новичок в GraphQl, и я следовал руководству GraphQL NodeJS по бэкэнду здесь: GraphQL Realtime Подписки. Все прошло хорошо, и я прошел обучение. Затем я выполнил руководство по VueJS с интерфейсом Apollo здесь: Vue & Apollo Subscriptions . Я решил использовать свой собственный сервер GraphQL, созданный из первого руководства, вместо использования GraphCool, который используется в руководстве по интерфейсу. Это та часть, где я застрял. Если я позвоню

subscription {
    Link(filter: {
      mutation_in: [CREATED]
    }) {
      node {
        id
        url
        description
      }
    }
}

в клиенте GraphiQL я получаю id, url и description, когда создаю новую ссылку на другой вкладке браузера с данными data: Link: node: {id: "59ef0bbeef32bb05692ee4b4", url: "http://new-test-url2.com", description: "Test description2"} (этот вызов подписки также выполняется в бэкэнд-руководстве). Но когда я звоню

subscription {
    Link(filter: {
      mutation_in: [CREATED]
    }) {
      node {
        id
        url
        description
        postedBy {
          id
          name
        }
        votes {
          id
          user {
             id
          }
        }
      }
    }
  }

Я получаю сообщение об ошибке:

{message: "Cannot destructure property `Users` of 'undefined' or 'null'.",…}
{message: "Cannot destructure property `Votes` of 'undefined' or 'null'.",…}

с данными data:{Link: {node: null}}

Я просто не могу найти ответ на эту проблему. Надеюсь, кто-то может мне в этом помочь. Это мой код:

схема / index.js

type Link {
    id: ID!
    url: String!
    description: String!
    postedBy: User
    votes: [Vote!]!
}

type User {
    id: ID!
    name: String!
    email: String!
    password: String!
    votes: [Vote!]!
}

type Vote {
    id: ID!
    user: User!
    link: Link!
}

type SigninPayload {
    token: String
    user: User
}

type Query {
    allLinks(filter: LinkFilter, skip: Int, first: Int): [Link!]!
}

type Mutation {
    createLink(url: String!, description: String!): Link

    createVote(linkId: ID!): Vote

    createUser(name: String!, email: String!, password: String!): User

    signinUser(email: String!, password: String!): SigninPayload!
}

type Subscription {
    Link(filter: LinkSubscriptionFilter): LinkSubscriptionPayload
    Vote(filter: VoteSubscriptionFilter): VoteSubscriptionPayload
}

input LinkSubscriptionFilter {
    mutation_in: [_ModelMutationType!]
}

input VoteSubscriptionFilter {
    mutation_in: [_ModelMutationType!]
}

type LinkSubscriptionPayload {
    mutation: _ModelMutationType!
    node: Link
}

type VoteSubscriptionPayload {
    mutation: _ModelMutationType!
    node: Vote
}

input LinkFilter {
    OR: [LinkFilter!]
    description_contains: String
    url_contains: String
}

enum _ModelMutationType {
    CREATED
    UPDATED
    DELETED
}

схема / resolvers.js:

Query: {
    allLinks: async (root, {filter, first, skip}, {mongo: {Links, Users}}) => {
        let query = filter ? {$or: buildFilters(filter)} : {};
        const cursor = Links.find(query);
        if (first) {
            cursor.limit(first);
        }

        if (skip) {
            cursor.skip(skip);
        }

        return cursor.toArray();
    },
},

Mutation: {
    createLink: async (root, data, {mongo: {Links}, user}) => {

        assertValidLink(data);
        const newLink = Object.assign({postedById: user && user._id}, data);
        const response = await Links.insert(newLink);

        newLink.id = response.insertedIds[0];

        pubsub.publish('Link', {Link: {mutation: 'CREATED', node: newLink}});

        return newLink;
    },

    createUser: async (root, data, {mongo: {Users}}) => {
        const newUser = {
            name: data.name,
            email: data.email,
            password: data.password,
        };

        const response = await Users.insert(newUser);

        return Object.assign({id: response.insertedIds[0]}, newUser);
    },

    createVote: async (root, data, {mongo: {Votes}, user}) => {
        const newVote = {
            userId: user && user._id,
            linkId: new ObjectID(data.linkId),
        };

        const response = await Votes.insert(newVote);

        return Object.assign({id: response.insertedIds[0]}, newVote);
    },

    signinUser: async (root, data, {mongo: {Users}}) => {
        const user = await Users.findOne({email: data.email});
        if (data.password === user.password) {
            return { token: `token-${user.email}`, user };
        }
    },
},

Subscription: {
    Link: {
        subscribe: () => pubsub.asyncIterator('Link'),
    },
},

Link: {
    id: root => root._id || root.id,

    // postedBy: async ({postedById}, data, {dataloaders: {userLoader}}) => {
    //     return await userLoader.load(postedById);
    // },

    postedBy: async ({postedById}, data, {mongo: {Users}}) => {
        return await Users.findOne({_id: postedById});
    },

    votes: async ({_id}, data, {mongo: {Votes}}) => {
        return await Votes.find({linkId: _id}).toArray();
    },
},

person Pixeldenker    schedule 24.10.2017    source источник


Ответы (1)


Надеясь, что вы смогли решить эту проблему, так как это было так давно, я только что понял это сегодня сам. Проблема здесь в том, что сервер подписки никогда не передавался в контексте, то есть загрузчиках данных / информации о пользователе.

В этом уроке (https://www.howtographql.com/graphql-js/4-connectors/) вы настраиваете контекст для экспресс-сервера, но он никогда не добавлялся на сервер подписки:

  const buildOptions = async (req,res) => {                                                     
const user = await authenticate(req, mongo.Users)                                           
return {                                                                                    
  context: {                                                                                
    dataloaders: buildDataloaders(mongo),                                                   
    mongo,                                                                                  
    user                                                                                    
  },                                                                                        
  formatError,                                                                              
  schema,                                                                                   
}                                                                                           

Чтобы добавить это на сервер подписки, я сам использовал следующие параметры:

const subscriptionBuildOptions = async (connectionParams,webSocket) => 
{                      
  return {                                                                                   
    dataloaders: buildDataloaders(mongo),                                                    
    mongo,                                                                                   
  }
}  

Затем я добавил это в SubscriptionServer, просто добавив параметр onConnect:

const server = createServer(app);                                                             
server.listen(PORT, () => {                                                                   
  SubscriptionServer.create(                                                                  
    {execute, subscribe, schema, onConnect: subscriptionBuildOptions},                        
    {server, path: '/subscriptions'},                                                         
  );                                                                                          
  console.log(`Hackernews GraphQL server running on port ${PORT}.`)                           
}); 

И это в основном должно заставить все работать. Я лично занимаюсь учебным пособием по responsejs, поэтому не уверен, какие отдельные проблемы возникнут для этого, но вызовы api учебного пособия reactjs были немного не синхронизированы.

person Marii    schedule 04.12.2017
comment
Большое спасибо! Это сработало! Я перестал пытаться, потому что мне это надоело, но ты просто сделал мой день. - person Pixeldenker; 05.12.2017