Repository: encode/starlette

Issue:
GraphQL response should not include error key if no error occured
The [GraphQL Spec](https://graphql.github.io/graphql-spec/June2018/#sec-Errors) states that:
> If no errors were encountered during the requested operation, the errors entry should not be present in the result.

Currently, if no errors are encountered, starlette will return `{"data": {...}, "errors": null}`. 
This is only a small thing, but enough to break some clients.

I have a PR for this incoming.


Discussion:

Resolving patch:
diff --git a/starlette/graphql.py b/starlette/graphql.py
--- a/starlette/graphql.py
+++ b/starlette/graphql.py
@@ -107,7 +107,9 @@ async def handle_graphql(self, request: Request) -> Response:
             if result.errors
             else None
         )
-        response_data = {"data": result.data, "errors": error_data}
+        response_data = {"data": result.data}
+        if error_data:
+            response_data["errors"] = error_data
         status_code = (
             status.HTTP_400_BAD_REQUEST if result.errors else status.HTTP_200_OK
         )


Test patch:
diff --git a/tests/test_graphql.py b/tests/test_graphql.py
--- a/tests/test_graphql.py
+++ b/tests/test_graphql.py
@@ -40,19 +40,19 @@ def resolve_whoami(self, info):
 def test_graphql_get():
     response = client.get("/?query={ hello }")
     assert response.status_code == 200
-    assert response.json() == {"data": {"hello": "Hello stranger"}, "errors": None}
+    assert response.json() == {"data": {"hello": "Hello stranger"}}
 
 
 def test_graphql_post():
     response = client.post("/?query={ hello }")
     assert response.status_code == 200
-    assert response.json() == {"data": {"hello": "Hello stranger"}, "errors": None}
+    assert response.json() == {"data": {"hello": "Hello stranger"}}
 
 
 def test_graphql_post_json():
     response = client.post("/", json={"query": "{ hello }"})
     assert response.status_code == 200
-    assert response.json() == {"data": {"hello": "Hello stranger"}, "errors": None}
+    assert response.json() == {"data": {"hello": "Hello stranger"}}
 
 
 def test_graphql_post_graphql():
@@ -60,7 +60,7 @@ def test_graphql_post_graphql():
         "/", data="{ hello }", headers={"content-type": "application/graphql"}
     )
     assert response.status_code == 200
-    assert response.json() == {"data": {"hello": "Hello stranger"}, "errors": None}
+    assert response.json() == {"data": {"hello": "Hello stranger"}}
 
 
 def test_graphql_post_invalid_media_type():
@@ -115,7 +115,7 @@ def test_add_graphql_route():
     client = TestClient(app)
     response = client.get("/?query={ hello }")
     assert response.status_code == 200
-    assert response.json() == {"data": {"hello": "Hello stranger"}, "errors": None}
+    assert response.json() == {"data": {"hello": "Hello stranger"}}
 
 
 def test_graphql_context():
@@ -127,7 +127,7 @@ def test_graphql_context():
         "/", json={"query": "{ whoami }"}, headers={"Authorization": "Bearer 123"}
     )
     assert response.status_code == 200
-    assert response.json() == {"data": {"whoami": "Jane"}, "errors": None}
+    assert response.json() == {"data": {"whoami": "Jane"}}
 
 
 class ASyncQuery(graphene.ObjectType):
@@ -145,7 +145,7 @@ def test_graphql_async():
     client = TestClient(async_app)
     response = client.get("/?query={ hello }")
     assert response.status_code == 200
-    assert response.json() == {"data": {"hello": "Hello stranger"}, "errors": None}
+    assert response.json() == {"data": {"hello": "Hello stranger"}}
 
 
 async_schema = graphene.Schema(query=ASyncQuery)
@@ -157,4 +157,4 @@ def test_graphql_async_old_style_executor():
     client = TestClient(old_style_async_app)
     response = client.get("/?query={ hello }")
     assert response.status_code == 200
-    assert response.json() == {"data": {"hello": "Hello stranger"}, "errors": None}
+    assert response.json() == {"data": {"hello": "Hello stranger"}}