apollographql / meteor-integration

🚀 meteor add apollo
http://dev.apollodata.com/core/meteor.html
108 stars 45 forks source link

"Can't wait without a fiber" error both in resolver and mutations #25

Closed wuzhuzhu closed 8 years ago

wuzhuzhu commented 8 years ago

root schema and resolver:

const rootSchema = `
   type RootQuery {
    user: User    
   ...
    allFeedbacks: [Feedback]
   }

   schema {
    query: RootQuery
  }
`

RootQuery: {
...

    async allFeedbacks(root, args, context) {
      const allFeedbacks = DB.Feedback.find({patientId: {$exists: 1}}).fetch()
      return await allFeedbacks
    },
...
}

feedback.js

export const schema = `
  type Feedback {
    _id: String
    patient: User
    opinion: String
    reply: String
  }
`;

export const resolvers = {
  Feedback: {
    _id  : ({_id}) => _id,
    patient: async ({patientId}) => {
      return await Meteor.users.findOne({_id:patientId})
    },
    opinion: ({opinion}) => opinion,
    reply: ({reply}) => reply,
  }
};

query in graphiql: { allFeedbacks { patient { username } _id opinion reply } }

Got result:

{ "data": { "allFeedbacks": [ { "patient": null, "_id": "nyNP9wh9D5qmZixbn", "opinion": "tesst", "reply": "testst" }, { "patient": null, "_id": "WgSoCgTGhHeZasFGQ", "opinion": "tesst", "reply": "testst" },

...

{ "message": "Can't wait without a fiber", "locations": [ { "line": 4, "column": 5 } ] }, }

It's strange when i test , the resolver could console log PatientId, Meteor, Meteor.users, But can't get the fetched data. I guess the problem is on async, but where is the problem?

Had checked sample repos, followed their pattern but no luck.

wuzhuzhu commented 8 years ago

same happened in mutation:

RootQuery :{
  ...
// works well 
    async oneFeedback(root, {feedbackId}, context) {
      const feedback = DB.Feedback.findOne({_id: feedbackId})
      return await feedback
    },
}

// mutate nothing but just for test , will throw an error
RootMutation: {
    async changeFeedbackText(root, {feedbackId, text}, context) {
      const feedback = DB.Feedback.findOne({_id: feedbackId})
      return await feedback
    }
// so does this one
   async changeFeedbackText(root, {feedbackId, text}, context) {
      DB.Feedback.update(feedbackId, {$set:{text: text}})
      return DB.Feedback.findOne(feedbackId)
    }
  }

Why same code in query don't throw an error?

wuzhuzhu commented 8 years ago

@helfer the two issue from apollo-server moved to here. Seems like same issue.

lorensr commented 8 years ago

@wuzhuzhu I've only seen await put directly in front of a findOne or fetch. So what about eg

const allFeedbacks = await DB.Feedback.find({patientId: {$exists: 1}}).fetch()

And when you make multiple calls that need to happen in series, do you need multiple awaits?

   async changeFeedbackText(root, {feedbackId, text}, context) {
      await DB.Feedback.update(feedbackId, {$set:{text: text}})
      return await DB.Feedback.findOne(feedbackId)
    }
  }
wuzhuzhu commented 8 years ago

@lorensr Tried your solution with no luck. always cant wait without a fiber.

Also tried to follow the update instruction migrate to apollo-server 0.2, comes with error when updating:

.../meteorApp/.meteor/local/build/programs/server/packages/modules.js:35015 W20160808-12:06:29.125(8)? (STDERR) const graphql_1 = require('graphql'); W20160808-12:06:29.126(8)? (STDERR) ^^^^^
W20160808-12:06:29.126(8)? (STDERR) SyntaxError: Use of const in strict mode.

lorensr commented 8 years ago

Sorry! await is working here:

https://github.com/apollostack/meteor-starter-kit/blob/master/imports/api/schema.js#L29

Could you try forking that repo and figuring out the minimum changes it takes to reproduce your error?

wuzhuzhu commented 8 years ago

fixed this problem. I should just not use async / await in my codes, since meteor fiber will do the wait part ...

I don't know why inject user profile to apollo request has to await, but codes blow works:

allFeedbacks(root, args, context) {
      const allFeedbacks = DB.Feedback.find({pateintId: {$exists: 1}}).fetch()
      return allFeedbacks
    },

feedback:

Feedback: {
    _id  : ({_id}) => _id,
    // patient: ({pateintId}) => pateintId,
    patient: ({pateintId}) => {
      return Meteor.users.findOne({_id:pateintId})
    },
    opinion: ({opinion}) => opinion,
    reply: ({reply}) => reply,
    text: ({text}) => text,
  }
wuzhuzhu commented 8 years ago

My experience is forget async/await syntax and everything works.... Emm.. Will close this issue after few more tests.

wuzhuzhu commented 8 years ago

@lorensr thank you for your answer all the same. My case is simple, async syntax on rootResolver:feedback broke the meteor fiber works on patient resolver which is nested in feedback. I don't find which situation should have to use async/ await

packages:

"apollo-client": "^0.3.17",
"apollo-server": "^0.1.5",
"express": "^4.14.0",
"graphql": "^0.6.0",
"graphql-tools": "^0.6.0",
smeijer commented 8 years ago

I had the same issue; to work around this: (not really a permanent solution, as I love to use async / await):

1) make sure to not load babel-polyfill on the server side. 2) make sure not to use async / await in resolvers

I imported babel-polyfill on the server side because Apollo was throwing an error: GraphQL error: regeneratorRuntime is not defined. I guess that's the real problem. Because async / await is still working now I removed the polyfill; just not in the resolvers.

@wuzhuzhu, you're right. Forget async/await and everything works. But I wouldn't close this issue. It is an issue that should be solved.

lorensr commented 8 years ago

@smeijer Is your app a Meteor app? And the problem is that when you use async/await in resolvers, the regeneratorRuntime error is thrown?

smeijer commented 8 years ago

@lorensr, exactly. Build using webpack:webpack

bwhitty commented 8 years ago

FWIW, I am using this package and I can write async resolvers and wait on Meteor collection methods just fine.

lorensr commented 8 years ago

Thanks Brady, are you using wepback:wepback or just apollo?

On Wednesday, August 10, 2016, Brady Whitten notifications@github.com wrote:

FWIW, I am using this package and I can write async resolvers and wait on Meteor collection methods just fine.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/apollostack/meteor-integration/issues/25#issuecomment-238922721, or mute the thread https://github.com/notifications/unsubscribe-auth/AAPVmGjA4jOQJDV1r0BoT0pFlyflMk0zks5qefwRgaJpZM4Jd3Sf .

bwhitty commented 8 years ago

I'm using the Meteor integration, so the Meteor build tool.

On Wed, Aug 10, 2016, 10:12 AM Loren Sands-Ramshaw notifications@github.com wrote:

Thanks Brady, are you using wepback:wepback or just apollo?

On Wednesday, August 10, 2016, Brady Whitten notifications@github.com wrote:

FWIW, I am using this package and I can write async resolvers and wait on Meteor collection methods just fine.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub < https://github.com/apollostack/meteor-integration/issues/25#issuecomment-238922721 , or mute the thread < https://github.com/notifications/unsubscribe-auth/AAPVmGjA4jOQJDV1r0BoT0pFlyflMk0zks5qefwRgaJpZM4Jd3Sf

.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/apollostack/meteor-integration/issues/25#issuecomment-238936155, or mute the thread https://github.com/notifications/unsubscribe-auth/ABf8fk6FoQwXWjcR-GICaqY6HV5ZUKb0ks5qegaTgaJpZM4Jd3Sf .

smeijer commented 8 years ago

It does seem to be a webpack thing. If I switch to meteor-hmr (ecmascript-hot) the async/await thing works just fine.

lorensr commented 8 years ago

Thanks @smeijer @bwhitty, moving to new issue: https://github.com/apollostack/meteor-integration/issues/27

smeijer commented 7 years ago

2) make sure not to use async / await in resolvers

One year ago I wrote this statement as a solution to the problem. Today I'm not using any foreign builders anymore. Just the meteor build tool as it supposed to be. Still; when I try to use async / await in my resolvers the Can't wait without a fiber error appears.

That's really unfortunate, as I do need it to be able to use dataloader without wrapping the thing in fibers all over the place.

acomito commented 6 years ago

@smeijer did you ever figure out how to use dataloader with meteor/apollo-server smoothly? @lorensr any tips?

smeijer commented 6 years ago

@acomito, Yes, I decided to use the rawCollections in my graphql resolvers, passed trough via the context. That way I don't have to deal with fibers at all. I can use async / await and dataloader as well.

That does mean that I'm not using any meteor specific code inside the graphql resolvers at all. As soon as I use meteor code there, the Can't wait without a fiber issue pops up.

vincro commented 6 years ago

@smeijer Per chance do you have some sample code you might share with use of Dataloader?

I find that when I run a mutation with await collection.loader.load(documentId) the "Cannot await without a Fiber" rears its ugly head, but the same with await collection.findOne{{_id: documentId}} works perfectly fine.

Very strange. TIA.

smeijer commented 6 years ago

I don't think I'm doing anything different with dataloader, I think it's in the collection. I'm using raw collection everywhere.

Simplified and off the cuff;

import { Mongo } from 'meteor/mongo';
import { createApolloServer } from 'meteor/apollo';
import DataLoader from 'dataloader';

const db = {
  // NOTE: rawCollection
  collection: new Mongo.Collection('coll').rawCollection(),
};

const models = {
  loader: new DataLoader(async ids => {
    // NOTE: I can not emphasize it enough; `collection` is a rawColleciton
    const items = await db.collection.find({ _id: { $in: ids } }).toArray();

    // you might want to do this more efficiently
    return ids.map(id => items.id === id);
  }),
};

createApolloServer(req => ({
  schema,    // import as usual
  resolvers, // import as usual

  context: ({ user, userId }) => ({
    user,
    userId,
    models,
    db,
  }),
}));
// resolver
export default {
  Query: {
    async item: (_, { id }, { userId, models }) {
      if (!userId) {
        throw new Error('unauthorized');
      }

      return await models.loader.load(id);
    },

    async item2: (_, { id }, { userId, db }) {
      // no dataloader, still using rawCollection
      return await db.collection.findOne({ _id: id });
    },
  }
}

Nice thing about this pattern is that:

Writing my code with async and await in combination with rawCollection-only, simpified my code a lot, and I've never seen an Meteor.bindEnvironment error since.

) Behind the scenes, they are still being wrapped in Fibers, as Meteor does this with Promises and thereby async / await statements. And Mongo methods all return Promises.*