We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Undefined variable issue in 0.77.6
With the latest release of #1213, this [line](https://github.com/strawberry-graphql/strawberry/blob/main/strawberry/types/nodes.py#L40) raises a KeyError when a variable is used in a query, but not defined in the payload under the key `variables`.

This used to work, and, I guess, is ok when the variable/argument is optional.

I do not know what the specs are, meaning if variable definition should be enforced all the way. If it's the way to go, maybe there should be a validation step/graceful way to handle such a case.

If not, `return info.variable_values.get(name)` should do the trick.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `strawberry/types/nodes.py`
Content:
```
1 """
2 Abstraction layer for graphql-core field nodes.
3 
4 Call `convert_sections` on a list of GraphQL `FieldNode`s, such as in `info.field_nodes`.
5 
6 If a node has only one useful value, it's value is inlined.
7 
8 If a list of nodes have unique names, it's transformed into a mapping.
9 Note Python dicts maintain ordering (for all supported versions).
10 """
11 
12 import dataclasses
13 from typing import Any, Dict, Iterable, List, Optional, Union
14 
15 from graphql import GraphQLResolveInfo
16 from graphql.language import (
17     ArgumentNode as GQLArgumentNode,
18     DirectiveNode as GQLDirectiveNode,
19     FieldNode as GQLFieldNode,
20     FragmentSpreadNode as GQLFragmentSpreadNode,
21     InlineFragmentNode as GQLInlineFragment,
22     InlineFragmentNode as GQLInlineFragmentNode,
23     ListValueNode as GQLListValueNode,
24     ObjectValueNode as GQLObjectValueNode,
25     ValueNode as GQLValueNode,
26     VariableNode as GQLVariableNode,
27 )
28 
29 
30 Arguments = Dict[str, Any]
31 Directives = Dict[str, Arguments]
32 Selection = Union["SelectedField", "FragmentSpread", "InlineFragment"]
33 
34 
35 def convert_value(info: GraphQLResolveInfo, node: GQLValueNode) -> Any:
36     """Return useful value from any node."""
37     if isinstance(node, GQLVariableNode):
38         # Look up variable
39         name = node.name.value
40         return info.variable_values[name]
41     if isinstance(node, GQLListValueNode):
42         return [convert_value(info, value) for value in node.values]
43     if isinstance(node, GQLObjectValueNode):
44         return {
45             field.name.value: convert_value(info, field.value) for field in node.fields
46         }
47     return getattr(node, "value", None)
48 
49 
50 def convert_arguments(
51     info: GraphQLResolveInfo, nodes: Iterable[GQLArgumentNode]
52 ) -> Arguments:
53     """Return mapping of arguments."""
54     return {node.name.value: convert_value(info, node.value) for node in nodes}
55 
56 
57 def convert_directives(
58     info: GraphQLResolveInfo, nodes: Iterable[GQLDirectiveNode]
59 ) -> Directives:
60     """Return mapping of directives."""
61     return {node.name.value: convert_arguments(info, node.arguments) for node in nodes}
62 
63 
64 def convert_selections(
65     info: GraphQLResolveInfo, field_nodes: List[GQLFieldNode]
66 ) -> List[Selection]:
67     """Return typed `Selection` based on node type."""
68     selections: List[Selection] = []
69     for node in field_nodes:
70         if isinstance(node, GQLFieldNode):
71             selections.append(SelectedField.from_node(info, node))
72         elif isinstance(node, GQLInlineFragment):
73             selections.append(InlineFragment.from_node(info, node))
74         elif isinstance(node, GQLFragmentSpreadNode):
75             selections.append(FragmentSpread.from_node(info, node))
76         else:
77             raise TypeError(f"Unknown node type: {node}")
78 
79     return selections
80 
81 
82 @dataclasses.dataclass
83 class FragmentSpread:
84     """Wrapper for a FragmentSpreadNode."""
85 
86     name: str
87     type_condition: str
88     directives: Directives
89     selections: List[Selection]
90 
91     @classmethod
92     def from_node(cls, info: GraphQLResolveInfo, node: GQLFragmentSpreadNode):
93         # Look up fragment
94         name = node.name.value
95         fragment = info.fragments[name]
96         return cls(
97             name=name,
98             directives=convert_directives(info, node.directives),
99             type_condition=fragment.type_condition.name.value,
100             selections=convert_selections(
101                 info, getattr(fragment.selection_set, "selections", [])
102             ),
103         )
104 
105 
106 @dataclasses.dataclass
107 class InlineFragment:
108     """Wrapper for a InlineFragmentNode."""
109 
110     type_condition: str
111     selections: List[Selection]
112     directives: Directives
113 
114     @classmethod
115     def from_node(cls, info: GraphQLResolveInfo, node: GQLInlineFragmentNode):
116         return cls(
117             type_condition=node.type_condition.name.value,
118             selections=convert_selections(
119                 info, getattr(node.selection_set, "selections", [])
120             ),
121             directives=convert_directives(info, node.directives),
122         )
123 
124 
125 @dataclasses.dataclass
126 class SelectedField:
127     """Wrapper for a FieldNode."""
128 
129     name: str
130     directives: Directives
131     arguments: Arguments
132     selections: List[Selection]
133     alias: Optional[str] = None
134 
135     @classmethod
136     def from_node(cls, info: GraphQLResolveInfo, node: GQLFieldNode):
137         return cls(
138             name=node.name.value,
139             directives=convert_directives(info, node.directives),
140             alias=getattr(node.alias, "value", None),
141             arguments=convert_arguments(info, node.arguments),
142             selections=convert_selections(
143                 info, getattr(node.selection_set, "selections", [])
144             ),
145         )
146
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/strawberry/types/nodes.py b/strawberry/types/nodes.py
--- a/strawberry/types/nodes.py
+++ b/strawberry/types/nodes.py
@@ -37,7 +37,7 @@
     if isinstance(node, GQLVariableNode):
         # Look up variable
         name = node.name.value
-        return info.variable_values[name]
+        return info.variable_values.get(name)
     if isinstance(node, GQLListValueNode):
         return [convert_value(info, value) for value in node.values]
     if isinstance(node, GQLObjectValueNode):

{"golden_diff": "diff --git a/strawberry/types/nodes.py b/strawberry/types/nodes.py\n--- a/strawberry/types/nodes.py\n+++ b/strawberry/types/nodes.py\n@@ -37,7 +37,7 @@\n     if isinstance(node, GQLVariableNode):\n         # Look up variable\n         name = node.name.value\n-        return info.variable_values[name]\n+        return info.variable_values.get(name)\n     if isinstance(node, GQLListValueNode):\n         return [convert_value(info, value) for value in node.values]\n     if isinstance(node, GQLObjectValueNode):\n", "issue": "Undefined variable issue in 0.77.6\nWith the latest release of #1213, this [line](https://github.com/strawberry-graphql/strawberry/blob/main/strawberry/types/nodes.py#L40) raises a KeyError when a variable is used in a query, but not defined in the payload under the key `variables`.\r\n\r\nThis used to work, and, I guess, is ok when the variable/argument is optional.\r\n\r\nI do not know what the specs are, meaning if variable definition should be enforced all the way. If it's the way to go, maybe there should be a validation step/graceful way to handle such a case.\r\n\r\nIf not, `return info.variable_values.get(name)` should do the trick.\n", "before_files": [{"content": "\"\"\"\nAbstraction layer for graphql-core field nodes.\n\nCall `convert_sections` on a list of GraphQL `FieldNode`s, such as in `info.field_nodes`.\n\nIf a node has only one useful value, it's value is inlined.\n\nIf a list of nodes have unique names, it's transformed into a mapping.\nNote Python dicts maintain ordering (for all supported versions).\n\"\"\"\n\nimport dataclasses\nfrom typing import Any, Dict, Iterable, List, Optional, Union\n\nfrom graphql import GraphQLResolveInfo\nfrom graphql.language import (\n    ArgumentNode as GQLArgumentNode,\n    DirectiveNode as GQLDirectiveNode,\n    FieldNode as GQLFieldNode,\n    FragmentSpreadNode as GQLFragmentSpreadNode,\n    InlineFragmentNode as GQLInlineFragment,\n    InlineFragmentNode as GQLInlineFragmentNode,\n    ListValueNode as GQLListValueNode,\n    ObjectValueNode as GQLObjectValueNode,\n    ValueNode as GQLValueNode,\n    VariableNode as GQLVariableNode,\n)\n\n\nArguments = Dict[str, Any]\nDirectives = Dict[str, Arguments]\nSelection = Union[\"SelectedField\", \"FragmentSpread\", \"InlineFragment\"]\n\n\ndef convert_value(info: GraphQLResolveInfo, node: GQLValueNode) -> Any:\n    \"\"\"Return useful value from any node.\"\"\"\n    if isinstance(node, GQLVariableNode):\n        # Look up variable\n        name = node.name.value\n        return info.variable_values[name]\n    if isinstance(node, GQLListValueNode):\n        return [convert_value(info, value) for value in node.values]\n    if isinstance(node, GQLObjectValueNode):\n        return {\n            field.name.value: convert_value(info, field.value) for field in node.fields\n        }\n    return getattr(node, \"value\", None)\n\n\ndef convert_arguments(\n    info: GraphQLResolveInfo, nodes: Iterable[GQLArgumentNode]\n) -> Arguments:\n    \"\"\"Return mapping of arguments.\"\"\"\n    return {node.name.value: convert_value(info, node.value) for node in nodes}\n\n\ndef convert_directives(\n    info: GraphQLResolveInfo, nodes: Iterable[GQLDirectiveNode]\n) -> Directives:\n    \"\"\"Return mapping of directives.\"\"\"\n    return {node.name.value: convert_arguments(info, node.arguments) for node in nodes}\n\n\ndef convert_selections(\n    info: GraphQLResolveInfo, field_nodes: List[GQLFieldNode]\n) -> List[Selection]:\n    \"\"\"Return typed `Selection` based on node type.\"\"\"\n    selections: List[Selection] = []\n    for node in field_nodes:\n        if isinstance(node, GQLFieldNode):\n            selections.append(SelectedField.from_node(info, node))\n        elif isinstance(node, GQLInlineFragment):\n            selections.append(InlineFragment.from_node(info, node))\n        elif isinstance(node, GQLFragmentSpreadNode):\n            selections.append(FragmentSpread.from_node(info, node))\n        else:\n            raise TypeError(f\"Unknown node type: {node}\")\n\n    return selections\n\n\n@dataclasses.dataclass\nclass FragmentSpread:\n    \"\"\"Wrapper for a FragmentSpreadNode.\"\"\"\n\n    name: str\n    type_condition: str\n    directives: Directives\n    selections: List[Selection]\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLFragmentSpreadNode):\n        # Look up fragment\n        name = node.name.value\n        fragment = info.fragments[name]\n        return cls(\n            name=name,\n            directives=convert_directives(info, node.directives),\n            type_condition=fragment.type_condition.name.value,\n            selections=convert_selections(\n                info, getattr(fragment.selection_set, \"selections\", [])\n            ),\n        )\n\n\n@dataclasses.dataclass\nclass InlineFragment:\n    \"\"\"Wrapper for a InlineFragmentNode.\"\"\"\n\n    type_condition: str\n    selections: List[Selection]\n    directives: Directives\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLInlineFragmentNode):\n        return cls(\n            type_condition=node.type_condition.name.value,\n            selections=convert_selections(\n                info, getattr(node.selection_set, \"selections\", [])\n            ),\n            directives=convert_directives(info, node.directives),\n        )\n\n\n@dataclasses.dataclass\nclass SelectedField:\n    \"\"\"Wrapper for a FieldNode.\"\"\"\n\n    name: str\n    directives: Directives\n    arguments: Arguments\n    selections: List[Selection]\n    alias: Optional[str] = None\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLFieldNode):\n        return cls(\n            name=node.name.value,\n            directives=convert_directives(info, node.directives),\n            alias=getattr(node.alias, \"value\", None),\n            arguments=convert_arguments(info, node.arguments),\n            selections=convert_selections(\n                info, getattr(node.selection_set, \"selections\", [])\n            ),\n        )\n", "path": "strawberry/types/nodes.py"}], "after_files": [{"content": "\"\"\"\nAbstraction layer for graphql-core field nodes.\n\nCall `convert_sections` on a list of GraphQL `FieldNode`s, such as in `info.field_nodes`.\n\nIf a node has only one useful value, it's value is inlined.\n\nIf a list of nodes have unique names, it's transformed into a mapping.\nNote Python dicts maintain ordering (for all supported versions).\n\"\"\"\n\nimport dataclasses\nfrom typing import Any, Dict, Iterable, List, Optional, Union\n\nfrom graphql import GraphQLResolveInfo\nfrom graphql.language import (\n    ArgumentNode as GQLArgumentNode,\n    DirectiveNode as GQLDirectiveNode,\n    FieldNode as GQLFieldNode,\n    FragmentSpreadNode as GQLFragmentSpreadNode,\n    InlineFragmentNode as GQLInlineFragment,\n    InlineFragmentNode as GQLInlineFragmentNode,\n    ListValueNode as GQLListValueNode,\n    ObjectValueNode as GQLObjectValueNode,\n    ValueNode as GQLValueNode,\n    VariableNode as GQLVariableNode,\n)\n\n\nArguments = Dict[str, Any]\nDirectives = Dict[str, Arguments]\nSelection = Union[\"SelectedField\", \"FragmentSpread\", \"InlineFragment\"]\n\n\ndef convert_value(info: GraphQLResolveInfo, node: GQLValueNode) -> Any:\n    \"\"\"Return useful value from any node.\"\"\"\n    if isinstance(node, GQLVariableNode):\n        # Look up variable\n        name = node.name.value\n        return info.variable_values.get(name)\n    if isinstance(node, GQLListValueNode):\n        return [convert_value(info, value) for value in node.values]\n    if isinstance(node, GQLObjectValueNode):\n        return {\n            field.name.value: convert_value(info, field.value) for field in node.fields\n        }\n    return getattr(node, \"value\", None)\n\n\ndef convert_arguments(\n    info: GraphQLResolveInfo, nodes: Iterable[GQLArgumentNode]\n) -> Arguments:\n    \"\"\"Return mapping of arguments.\"\"\"\n    return {node.name.value: convert_value(info, node.value) for node in nodes}\n\n\ndef convert_directives(\n    info: GraphQLResolveInfo, nodes: Iterable[GQLDirectiveNode]\n) -> Directives:\n    \"\"\"Return mapping of directives.\"\"\"\n    return {node.name.value: convert_arguments(info, node.arguments) for node in nodes}\n\n\ndef convert_selections(\n    info: GraphQLResolveInfo, field_nodes: List[GQLFieldNode]\n) -> List[Selection]:\n    \"\"\"Return typed `Selection` based on node type.\"\"\"\n    selections: List[Selection] = []\n    for node in field_nodes:\n        if isinstance(node, GQLFieldNode):\n            selections.append(SelectedField.from_node(info, node))\n        elif isinstance(node, GQLInlineFragment):\n            selections.append(InlineFragment.from_node(info, node))\n        elif isinstance(node, GQLFragmentSpreadNode):\n            selections.append(FragmentSpread.from_node(info, node))\n        else:\n            raise TypeError(f\"Unknown node type: {node}\")\n\n    return selections\n\n\n@dataclasses.dataclass\nclass FragmentSpread:\n    \"\"\"Wrapper for a FragmentSpreadNode.\"\"\"\n\n    name: str\n    type_condition: str\n    directives: Directives\n    selections: List[Selection]\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLFragmentSpreadNode):\n        # Look up fragment\n        name = node.name.value\n        fragment = info.fragments[name]\n        return cls(\n            name=name,\n            directives=convert_directives(info, node.directives),\n            type_condition=fragment.type_condition.name.value,\n            selections=convert_selections(\n                info, getattr(fragment.selection_set, \"selections\", [])\n            ),\n        )\n\n\n@dataclasses.dataclass\nclass InlineFragment:\n    \"\"\"Wrapper for a InlineFragmentNode.\"\"\"\n\n    type_condition: str\n    selections: List[Selection]\n    directives: Directives\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLInlineFragmentNode):\n        return cls(\n            type_condition=node.type_condition.name.value,\n            selections=convert_selections(\n                info, getattr(node.selection_set, \"selections\", [])\n            ),\n            directives=convert_directives(info, node.directives),\n        )\n\n\n@dataclasses.dataclass\nclass SelectedField:\n    \"\"\"Wrapper for a FieldNode.\"\"\"\n\n    name: str\n    directives: Directives\n    arguments: Arguments\n    selections: List[Selection]\n    alias: Optional[str] = None\n\n    @classmethod\n    def from_node(cls, info: GraphQLResolveInfo, node: GQLFieldNode):\n        return cls(\n            name=node.name.value,\n            directives=convert_directives(info, node.directives),\n            alias=getattr(node.alias, \"value\", None),\n            arguments=convert_arguments(info, node.arguments),\n            selections=convert_selections(\n                info, getattr(node.selection_set, \"selections\", [])\n            ),\n        )\n", "path": "strawberry/types/nodes.py"}]}