graphql-php: Using with dataloader-php throws "Could not resolve promise" Exception
I have three types
type Questionnaire {
id: Int!
blocks: [Int]!
}
type Block {
id: Int!
wrappers: [Wrapper]!
}
type Wrapper {
id: Int!
questions: [Question]!
}
type Question {
id: Int!
}
type Query {
questionnaire(questionnaireId: Int!): Questionnaire
wrapper(wrapperId: Int!): Wrapper
}
Every field that returns a list uses a DataLoader (like in https://github.com/overblog/dataloader-php#using-with-webonyxgraphql). Each DataLoader is identical, despite the tables from which the dataloaders fetch their data.
The following query works fine:
query questionnaire {
questionnaire(questionnaireId: 1) { # no dataloader
blocks { # resolver uses dataloader
wrappers { # resolvers uses dataloader
id
}
}
}
}
However going one level deeper results in an exception.
query questionnaire {
questionnaire(questionnaireId: 1) { # no dataloader
blocks { # resolver uses dataloader
wrappers { # resolvers uses dataloader
id
questions { # resolver uses dataloader
id
}
}
}
}
}
InvariantViolation in SyncPromiseAdapter.php line 151:
Could not resolve promise
When I log the status of the promise I get pending.
According to the code this should not happen. https://github.com/webonyx/graphql-php/blob/master/src/Executor/Promise/Adapter/SyncPromiseAdapter.php#L132
When I use the following query (only 2 dataloader) everything is behaving like excpected:
query wrapper {
wrapper(wrapperId: 1) { # resolvers uses dataloader
id
questions { # resolver uses dataloader
id
}
}
}
Edit: This also happens in this resolver function where I chain multiple promises (I am using laravel):
public function resolveIsValidField($questionnaire, $args, $context) {
$questionnaireId = is_array($questionnaire) ? $questionnaire['id'] : $questionnaire->id;
/** @var BlockRepositoryInterface $blockRepository */
$blockRepository = $context['repositories']['blocks'];
/** @var WrapperRepositoryInterface $wrapperRepository */
$wrapperRepository = $context['repositories']['wrappers'];
/** @var QuestionRepositoryInterface $questionRepository */
$questionRepository = $context['repositories']['questions'];
$blocks = null;
$wrappers = null;
return $blockRepository->findWhereQuestionnaireId($questionnaireId)
->then(function (Collection $blockRecords) use (&$blocks, $wrapperRepository) {
$blocks = $blockRecords;
$bockIds = $blocks->map(function (Block $block) {
return $block->id;
})->toArray();
return $wrapperRepository->getWhereBlockIds($bockIds);
})
->then(function ($wrapperRecords) use (&$wrappers, $questionRepository) {
$wrappers = collect($wrapperRecords)->flatten();
$wrapperIds = $wrappers->pluck('id');
return $questionRepository->getWhereWrapperIds($wrapperIds->toArray());
})
->then(function ($questionRecords) use (&$blocks, &$wrappers) {
$questions = collect($questionRecords)->flatten();
return isQuestionnaireValid($blocks, $wrappers, $questions);
});
}
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Comments: 18 (11 by maintainers)
I’ll submit it, thank you @vladar 😉
Works for me, pragmatically %) Can anyone prepare PR for this? I’ll merge it.
I forked your repo and replaced DataLoaders with regular
GraphQL\Deferred
calls. It works as expected.I guess the bug is somewhere on DataLoader side.
@mcg-web Here is my reproduction repository: https://github.com/n1ru4l/laravel-graphql-dataloader-php-exception-reproduction
The relevant files are the Service Provider (
app/Providers/AppServiceProvider.php
), the types and queriesapp/GraphQL/*
and the DataLoadersapp/DataLoader/*.php
You will not need any database setup just use
php artisan serve
.You can navigate to
graphiql
on/graphiql
.This query fails:
This query works:
Thanks! Merged and released 0.9.14
here a solution that works:
@vladar tell me what do you think of this solution please?
@mcg-web If I add
DataLoader::await()
to queue progression, it works, e.g.:So the whole queue must be progressed together. I guess you guys will have to write your own
wait
implementation to make it behave as expected.Obviously, I am not 100% sure that this is enough to fix it (as I am not aware of
DataLoader::await
implementation details).But let me know if I can make it easier for you somehow (e.g. by changing an interface of SyncPromiseAdapter or maybe changing how Executor calls
wait
).