Skip to content

Commit

Permalink
Simplify exposing root type Query in nested results (#2384)
Browse files Browse the repository at this point in the history
  • Loading branch information
spawnia authored Apr 18, 2023
1 parent 836eebe commit 729f712
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 1 deletion.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ You can find and compare releases at the [GitHub release page](https://github.co

## Unreleased

## v6.7.0

### Added

- Simplify exposing root type `Query` in nested results https://github.com/nuwave/lighthouse/pull/2384

## v6.6.1

### Fixed
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ help: ## Displays this list of targets with descriptions
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(firstword $(MAKEFILE_LIST)) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}'

.PHONY: setup
setup: build vendor ## Setup the local environment
setup: build docs/node_modules vendor ## Setup the local environment

.PHONY: build
build: ## Build the local Docker containers
Expand Down Expand Up @@ -66,3 +66,6 @@ release: ## Prepare the docs for a new release
.PHONY: docs
docs: up ## Render the docs in a development server
${dcnode} yarn run start

docs/node_modules: up docs/package.json docs/yarn.lock ## Install yarn dependencies
${dcnode} yarn
1 change: 1 addition & 0 deletions docs/6/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module.exports = [
"the-basics/types",
"the-basics/fields",
"the-basics/directives",
"the-basics/best-practices",
],
},
{
Expand Down
38 changes: 38 additions & 0 deletions docs/6/the-basics/best-practices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Best Practices

When starting out with developing a GraphQL API, it is a good idea to look at existing best practices.
We recommend you use [GraphQL Rules](https://graphql-rules.com) and the following tips as a starting point
to develop a set of guidelines that works for you.

## In the mutation response, return a field of type Query

If you decide to [return the `Query` object in every mutation payload](https://graphql-rules.com/rules/mutation-payload-query),
Lighthouse makes it very easy.

```graphql
type Mutation {
likePost(id: ID!): LikePostResult!
}

type LikePostResult {
record: Post!
query: Query!
}
```

Lighthouse automatically resolves the `Query` type, your mutation resolver only has to focus on its specific work.

```php
namespace App\GraphQL\Mutations;

final class LikePost
{
/** @param array{id: string} $args */
public function __invoke(mixed $root, array $args): array
{
// do the main work

return ['record' => $post];
}
}
```
1 change: 1 addition & 0 deletions docs/master/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module.exports = [
"the-basics/types",
"the-basics/fields",
"the-basics/directives",
"the-basics/best-practices",
],
},
{
Expand Down
38 changes: 38 additions & 0 deletions docs/master/the-basics/best-practices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Best Practices

When starting out with developing a GraphQL API, it is a good idea to look at existing best practices.
We recommend you use [GraphQL Rules](https://graphql-rules.com) and the following tips as a starting point
to develop a set of guidelines that works for you.

## In the mutation response, return a field of type Query

If you decide to [return the `Query` object in every mutation payload](https://graphql-rules.com/rules/mutation-payload-query),
Lighthouse makes it very easy.

```graphql
type Mutation {
likePost(id: ID!): LikePostResult!
}

type LikePostResult {
record: Post!
query: Query!
}
```

Lighthouse automatically resolves the `Query` type, your mutation resolver only has to focus on its specific work.

```php
namespace App\GraphQL\Mutations;

final class LikePost
{
/** @param array{id: string} $args */
public function __invoke(mixed $root, array $args): array
{
// do the main work

return ['record' => $post];
}
}
```
7 changes: 7 additions & 0 deletions src/Schema/ResolverProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Container\Container;
use Illuminate\Support\Str;
use Nuwave\Lighthouse\Exceptions\DefinitionException;
use Nuwave\Lighthouse\Schema\AST\ASTHelper;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\ProvidesResolver;
use Nuwave\Lighthouse\Support\Utils;
Expand All @@ -22,6 +23,12 @@ public function provideResolver(FieldValue $fieldValue): \Closure
$this->throwMissingResolver($fieldValue);
}

// Return any non-null value to continue nested field resolution
// when the root Query type is returned as part of the result.
if (ASTHelper::getUnderlyingTypeName($fieldValue->getField()) === RootType::QUERY) {
return static fn () => true;
}

return \Closure::fromCallable(
Executor::getDefaultFieldResolver(),
);
Expand Down
44 changes: 44 additions & 0 deletions tests/Integration/Schema/ResolverProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,48 @@ public function testNonRootFields(): void
],
]);
}

/** @see https://graphql-rules.com/rules/mutation-payload-query */
public function testRootQueryMutationPayload(): void
{
$fooResult = 1;
$this->mockResolver(['result' => $fooResult], 'foo');
$barResult = 2;
$this->mockResolver($barResult, 'bar');

$this->schema = /** @lang GraphQL */ '
type Query {
bar: Int! @mock(key: "bar")
}
type Mutation {
foo: FooResult! @mock(key: "foo")
}
type FooResult {
result: Int!
query: Query!
}
';

$this->graphQL(/** @lang GraphQL */ '
mutation {
foo {
result
query {
bar
}
}
}
')->assertJson([
'data' => [
'foo' => [
'result' => $fooResult,
'query' => [
'bar' => $barResult,
],
],
],
]);
}
}

0 comments on commit 729f712

Please sign in to comment.