diff --git a/vendor/magento/module-graph-ql/Controller/GraphQl.php b/vendor/magento/module-graph-ql/Controller/GraphQl.php
index f03079c89bc68..ab766de9d83a6 100644
--- a/vendor/magento/module-graph-ql/Controller/GraphQl.php
+++ b/vendor/magento/module-graph-ql/Controller/GraphQl.php
@@ -1,14 +1,14 @@
 <?php
-
 /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
+ * Copyright 2017 Adobe
+ * All Rights Reserved.
  */
-
 declare(strict_types=1);
 
 namespace Magento\GraphQl\Controller;
 
+use GraphQL\Error\FormattedError;
+use GraphQL\Error\SyntaxError;
 use Magento\Framework\App\Area;
 use Magento\Framework\App\AreaList;
 use Magento\Framework\App\FrontControllerInterface;
@@ -19,7 +19,9 @@
 use Magento\Framework\App\ResponseInterface;
 use Magento\Framework\Controller\Result\JsonFactory;
 use Magento\Framework\GraphQl\Exception\ExceptionFormatter;
+use Magento\Framework\GraphQl\Exception\GraphQlInputException;
 use Magento\Framework\GraphQl\Query\Fields as QueryFields;
+use Magento\Framework\GraphQl\Query\QueryParser;
 use Magento\Framework\GraphQl\Query\QueryProcessor;
 use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
 use Magento\Framework\GraphQl\Schema\SchemaGeneratorInterface;
@@ -41,6 +43,7 @@ class GraphQl implements FrontControllerInterface
     /**
      * @var \Magento\Framework\Webapi\Response
      * @deprecated 100.3.2
+     * @see no replacement
      */
     private $response;
 
@@ -66,7 +69,8 @@ class GraphQl implements FrontControllerInterface
 
     /**
      * @var ContextInterface
-     * @deprecated 100.3.3 $contextFactory is used for creating Context object
+     * @deprecated 100.3.3
+     * @see $contextFactory is used for creating Context object
      */
     private $resolverContext;
 
@@ -110,6 +114,11 @@ class GraphQl implements FrontControllerInterface
      */
     private $areaList;
 
+    /**
+     * @var QueryParser
+     */
+    private $queryParser;
+
     /**
      * @param Response $response
      * @param SchemaGeneratorInterface $schemaGenerator
@@ -125,6 +134,7 @@ class GraphQl implements FrontControllerInterface
      * @param LogData|null $logDataHelper
      * @param LoggerPool|null $loggerPool
      * @param AreaList|null $areaList
+     * @param QueryParser|null $queryParser
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
@@ -141,7 +151,8 @@ public function __construct(
         ContextFactoryInterface $contextFactory = null,
         LogData $logDataHelper = null,
         LoggerPool $loggerPool = null,
-        AreaList $areaList = null
+        AreaList $areaList = null,
+        QueryParser $queryParser = null
     ) {
         $this->response = $response;
         $this->schemaGenerator = $schemaGenerator;
@@ -157,6 +168,7 @@ public function __construct(
         $this->logDataHelper = $logDataHelper ?: ObjectManager::getInstance()->get(LogData::class);
         $this->loggerPool = $loggerPool ?: ObjectManager::getInstance()->get(LoggerPool::class);
         $this->areaList = $areaList ?: ObjectManager::getInstance()->get(AreaList::class);
+        $this->queryParser = $queryParser ?: ObjectManager::getInstance()->get(QueryParser::class);
     }
 
     /**
@@ -172,36 +184,49 @@ public function dispatch(RequestInterface $request): ResponseInterface
 
         $statusCode = 200;
         $jsonResult = $this->jsonFactory->create();
-        $data = $this->getDataFromRequest($request);
-        $result = [];
-
+        $data = [];
+        $result = null;
         $schema = null;
+        $query = '';
+
         try {
+            $data = $this->getDataFromRequest($request);
+            $query = $data['query'] ?? '';
+
             /** @var Http $request */
             $this->requestProcessor->validateRequest($request);
+            if ($request->isGet() || $request->isPost()) {
+                $parsedQuery = $this->queryParser->parse($query);
+                $data['parsedQuery'] = $parsedQuery;
 
-            $query = $data['query'] ?? '';
-            $variables = $data['variables'] ?? null;
-
-            // We must extract queried field names to avoid instantiation of unnecessary fields in webonyx schema
-            // Temporal coupling is required for performance optimization
-            $this->queryFields->setQuery($query, $variables);
-            $schema = $this->schemaGenerator->generate();
-
-            $result = $this->queryProcessor->process(
-                $schema,
-                $query,
-                $this->contextFactory->create(),
-                $data['variables'] ?? []
-            );
+                // We must extract queried field names to avoid instantiation of unnecessary fields in webonyx schema
+                // Temporal coupling is required for performance optimization
+                $this->queryFields->setQuery($parsedQuery, $data['variables'] ?? null);
+                $schema = $this->schemaGenerator->generate();
+
+                $result = $this->queryProcessor->process(
+                    $schema,
+                    $parsedQuery,
+                    $this->contextFactory->create(),
+                    $data['variables'] ?? []
+                );
+            }
+        } catch (SyntaxError|GraphQlInputException $error) {
+            $result = [
+                'errors' => [FormattedError::createFromException($error)],
+            ];
+            $statusCode = 400;
         } catch (\Exception $error) {
-            $result['errors'] = isset($result['errors']) ? $result['errors'] : [];
-            $result['errors'][] = $this->graphQlError->create($error);
+            $result = [
+                'errors' => [$this->graphQlError->create($error)],
+            ];
             $statusCode = ExceptionFormatter::HTTP_GRAPH_QL_SCHEMA_ERROR_STATUS;
         }
 
         $jsonResult->setHttpResponseCode($statusCode);
-        $jsonResult->setData($result);
+        if ($result !== null) {
+            $jsonResult->setData($result);
+        }
         $jsonResult->renderResult($this->httpResponse);
 
         // log information about the query, unless it is an introspection query
@@ -218,20 +243,25 @@ public function dispatch(RequestInterface $request): ResponseInterface
      *
      * @param RequestInterface $request
      * @return array
+     * @throws GraphQlInputException
      */
     private function getDataFromRequest(RequestInterface $request): array
     {
-        /** @var Http $request */
-        if ($request->isPost()) {
-            $data = $this->jsonSerializer->unserialize($request->getContent());
-        } elseif ($request->isGet()) {
-            $data = $request->getParams();
-            $data['variables'] = isset($data['variables']) ?
-                $this->jsonSerializer->unserialize($data['variables']) : null;
-            $data['variables'] = is_array($data['variables']) ?
-                $data['variables'] : null;
-        } else {
-            return [];
+        try {
+            /** @var Http $request */
+            if ($request->isPost()) {
+                $data = $request->getContent() ? $this->jsonSerializer->unserialize($request->getContent()) : [];
+            } elseif ($request->isGet()) {
+                $data = $request->getParams();
+                $data['variables'] = isset($data['variables']) ?
+                    $this->jsonSerializer->unserialize($data['variables']) : null;
+                $data['variables'] = is_array($data['variables']) ?
+                    $data['variables'] : null;
+            } else {
+                $data = [];
+            }
+        } catch (\InvalidArgumentException $e) {
+            throw new GraphQlInputException(__('Unable to parse the request.'), $e);
         }
 
         return $data;
diff --git a/vendor/magento/module-graph-ql/Controller/HttpRequestValidator/HttpVerbValidator.php b/vendor/magento/module-graph-ql/Controller/HttpRequestValidator/HttpVerbValidator.php
index d42676f5dd1b4..56351c7711cec 100644
--- a/vendor/magento/module-graph-ql/Controller/HttpRequestValidator/HttpVerbValidator.php
+++ b/vendor/magento/module-graph-ql/Controller/HttpRequestValidator/HttpVerbValidator.php
@@ -9,12 +9,12 @@
 
 use GraphQL\Language\AST\Node;
 use GraphQL\Language\AST\NodeKind;
-use GraphQL\Language\Parser;
-use GraphQL\Language\Source;
 use GraphQL\Language\Visitor;
 use Magento\Framework\App\HttpRequestInterface;
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\App\Request\Http;
 use Magento\Framework\GraphQl\Exception\GraphQlInputException;
+use Magento\Framework\GraphQl\Query\QueryParser;
 use Magento\Framework\Phrase;
 use Magento\GraphQl\Controller\HttpRequestValidatorInterface;
 
@@ -23,6 +23,19 @@
  */
 class HttpVerbValidator implements HttpRequestValidatorInterface
 {
+    /**
+     * @var QueryParser
+     */
+    private $queryParser;
+
+    /**
+     * @param QueryParser|null $queryParser
+     */
+    public function __construct(QueryParser $queryParser = null)
+    {
+        $this->queryParser = $queryParser ?: ObjectManager::getInstance()->get(QueryParser::class);
+    }
+
     /**
      * Check if request is using correct verb for query or mutation
      *
@@ -37,9 +50,9 @@ public function validate(HttpRequestInterface $request): void
             $query = $request->getParam('query', '');
             if (!empty($query)) {
                 $operationType = '';
-                $queryAst = Parser::parse(new Source($query ?: '', 'GraphQL'));
+                $parsedQuery = $this->queryParser->parse($query);
                 Visitor::visit(
-                    $queryAst,
+                    $parsedQuery,
                     [
                         'leave' => [
                             NodeKind::OPERATION_DEFINITION => function (Node $node) use (&$operationType) {
diff --git a/vendor/magento/module-graph-ql/Helper/Query/Logger/LogData.php b/vendor/magento/module-graph-ql/Helper/Query/Logger/LogData.php
index 91e2518bfc634..fd45ef93cf13b 100644
--- a/vendor/magento/module-graph-ql/Helper/Query/Logger/LogData.php
+++ b/vendor/magento/module-graph-ql/Helper/Query/Logger/LogData.php
@@ -8,13 +8,14 @@
 namespace Magento\GraphQl\Helper\Query\Logger;
 
 use GraphQL\Error\SyntaxError;
+use GraphQL\Language\AST\DocumentNode;
 use GraphQL\Language\AST\Node;
 use GraphQL\Language\AST\NodeKind;
-use GraphQL\Language\Parser;
-use GraphQL\Language\Source;
 use GraphQL\Language\Visitor;
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\App\RequestInterface;
 use Magento\Framework\App\Response\Http as HttpResponse;
+use Magento\Framework\GraphQl\Query\QueryParser;
 use Magento\Framework\GraphQl\Schema;
 use Magento\GraphQl\Model\Query\Logger\LoggerInterface;
 
@@ -23,6 +24,19 @@
  */
 class LogData
 {
+    /**
+     * @var QueryParser
+     */
+    private $queryParser;
+
+    /**
+     * @param QueryParser|null $queryParser
+     */
+    public function __construct(QueryParser $queryParser = null)
+    {
+        $this->queryParser = $queryParser ?: ObjectManager::getInstance()->get(QueryParser::class);
+    }
+
     /**
      * Extracts relevant information about the request
      *
@@ -43,7 +57,7 @@ public function getLogData(
         $logData = array_merge($logData, $this->gatherRequestInformation($request));
 
         try {
-            $complexity = $this->getFieldCount($data['query'] ?? '');
+            $complexity = $this->getFieldCount($data['parsedQuery'] ?? $data['query'] ?? '');
             $logData[LoggerInterface::COMPLEXITY] = $complexity;
             if ($schema) {
                 $logData = array_merge($logData, $this->gatherQueryInformation($schema));
@@ -114,18 +128,20 @@ private function gatherResponseInformation(HttpResponse $response) : array
      *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      *
-     * @param string $query
+     * @param DocumentNode|string $query
      * @return int
      * @throws SyntaxError
-     * @throws /Exception
+     * @throws \Exception
      */
-    private function getFieldCount(string $query): int
+    private function getFieldCount(DocumentNode|string $query): int
     {
         if (!empty($query)) {
             $totalFieldCount = 0;
-            $queryAst = Parser::parse(new Source($query ?: '', 'GraphQL'));
+            if (is_string($query)) {
+                $query = $this->queryParser->parse($query);
+            }
             Visitor::visit(
-                $queryAst,
+                $query,
                 [
                     'leave' => [
                         NodeKind::FIELD => function (Node $node) use (&$totalFieldCount) {
diff --git a/vendor/magento/framework/GraphQl/Query/Fields.php b/vendor/magento/framework/GraphQl/Query/Fields.php
index 78062effe3d41..5732f9d75d985 100644
--- a/vendor/magento/framework/GraphQl/Query/Fields.php
+++ b/vendor/magento/framework/GraphQl/Query/Fields.php
@@ -7,8 +7,10 @@
 
 namespace Magento\Framework\GraphQl\Query;
 
+use GraphQL\Language\AST\DocumentNode;
 use GraphQL\Language\AST\Node;
 use GraphQL\Language\AST\NodeKind;
+use Magento\Framework\App\ObjectManager;
 
 /**
  * This class holds a list of all queried fields and is used to enable performance optimization for schema loading.
@@ -20,21 +22,36 @@ class Fields
      */
     private $fieldsUsedInQuery = [];
 
+    /**
+     * @var QueryParser
+     */
+    private $queryParser;
+
+    /**
+     * @param QueryParser|null $queryParser
+     */
+    public function __construct(QueryParser $queryParser = null)
+    {
+        $this->queryParser = $queryParser ?: ObjectManager::getInstance()->get(QueryParser::class);
+    }
+
     /**
      * Set Query for extracting list of fields.
      *
-     * @param string $query
+     * @param DocumentNode|string $query
      * @param array|null $variables
      *
      * @return void
      */
-    public function setQuery($query, array $variables = null)
+    public function setQuery(DocumentNode|string $query, array $variables = null)
     {
         $queryFields = [];
         try {
-            $queryAst = \GraphQL\Language\Parser::parse(new \GraphQL\Language\Source($query ?: '', 'GraphQL'));
+            if (is_string($query)) {
+                $query = $this->queryParser->parse($query);
+            }
             \GraphQL\Language\Visitor::visit(
-                $queryAst,
+                $query,
                 [
                     'leave' => [
                         NodeKind::NAME => function (Node $node) use (&$queryFields) {
diff --git a/vendor/magento/framework/GraphQl/Query/QueryComplexityLimiter.php b/vendor/magento/framework/GraphQl/Query/QueryComplexityLimiter.php
index 9a2396f5d2f3d..f4359bfc9e457 100644
--- a/vendor/magento/framework/GraphQl/Query/QueryComplexityLimiter.php
+++ b/vendor/magento/framework/GraphQl/Query/QueryComplexityLimiter.php
@@ -7,10 +7,9 @@
 
 namespace Magento\Framework\GraphQl\Query;
 
-use GraphQL\Language\AST\NodeKind;
+use GraphQL\Language\AST\DocumentNode;
 use GraphQL\Language\AST\FieldNode;
-use GraphQL\Language\Parser;
-use GraphQL\Language\Source;
+use GraphQL\Language\AST\NodeKind;
 use GraphQL\Language\Visitor;
 use GraphQL\Language\AST\SelectionSetNode;
 use GraphQL\Validator\DocumentValidator;
@@ -47,6 +46,11 @@ class QueryComplexityLimiter
      */
     private $introspectionConfig;
 
+    /**
+     * @var QueryParser
+     */
+    private $queryParser;
+
     /**
      * @var MaximumAliasConfiguration
      */
@@ -56,17 +60,20 @@ class QueryComplexityLimiter
      * @param int $queryDepth
      * @param int $queryComplexity
      * @param IntrospectionConfiguration $introspectionConfig
+     * @param QueryParser|null $queryParser
      * @param MaximumAliasConfiguration|null $maximumAliasConfiguration
      */
     public function __construct(
         int $queryDepth,
         int $queryComplexity,
         IntrospectionConfiguration $introspectionConfig,
-        MaximumAliasConfiguration $maximumAliasConfiguration = null
+        ?QueryParser $queryParser = null,
+        ?MaximumAliasConfiguration $maximumAliasConfiguration = null
     ) {
         $this->queryDepth = $queryDepth;
         $this->queryComplexity = $queryComplexity;
         $this->introspectionConfig = $introspectionConfig;
+        $this->queryParser = $queryParser ?: ObjectManager::getInstance()->get(QueryParser::class);
         $this->maximumAliasConfiguration = $maximumAliasConfiguration ?:
             ObjectManager::getInstance()->get(MaximumAliasConfiguration::class);
     }
@@ -92,16 +99,19 @@ public function execute(): void
      * This is necessary for performance optimization, as extremely large queries require a substantial
      * amount of time to fully validate and can affect server performance.
      *
-     * @param string $query
+     * @param DocumentNode|string $query
      * @throws GraphQlInputException
      */
-    public function validateFieldCount(string $query): void
+    public function validateFieldCount(DocumentNode|string $query): void
     {
         if (!empty($query)) {
             $totalFieldCount = 0;
-            $queryAst = Parser::parse(new Source($query ?: '', 'GraphQL'));
+            if (is_string($query)) {
+                $query = $this->queryParser->parse($query);
+            }
+
             Visitor::visit(
-                $queryAst,
+                $query,
                 [
                     'leave' => [
                         NodeKind::FIELD => function () use (&$totalFieldCount) {
@@ -127,18 +137,16 @@ public function validateFieldCount(string $query): void
      * This is necessary for performance optimization, as extremely large number of alias in a request
      * require a substantial amount of resource can affect server performance.
      *
-     * @param string $query
+     * @param DocumentNode $query
      * @return void
      * @throws GraphQlInputException
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function validateAliasCount(string $query): void
+    public function validateAliasCount(DocumentNode $query): void
     {
         if ($this->maximumAliasConfiguration->isMaximumAliasLimitEnabled()) {
             $aliasCount = 0;
-            $query = Parser::parse(new Source($query ?: '', 'GraphQL'));
             foreach ($query->definitions as $definition) {
-                
                 if (property_exists($definition, 'selectionSet')) {
                     $aliasCount += $this->countAliasesInSelectionSet($definition->selectionSet);
                 }
@@ -157,10 +165,10 @@ public function validateAliasCount(string $query): void
     /**
      * Performs counting of aliases in a graphql request
      *
-     * @param SelectionSetNode $selectionSet
+     * @param ?SelectionSetNode $selectionSet
      * @return int
      */
-    private function countAliasesInSelectionSet(SelectionSetNode $selectionSet): int
+    private function countAliasesInSelectionSet(?SelectionSetNode $selectionSet): int
     {
         if ($selectionSet === null) {
             return 0;
@@ -179,6 +187,7 @@ private function countAliasesInSelectionSet(SelectionSetNode $selectionSet): int
                 }
             }
         }
+
         return $aliasCount;
     }
 }
diff --git a/vendor/magento/framework/GraphQl/Query/QueryParser.php b/vendor/magento/framework/GraphQl/Query/QueryParser.php
new file mode 100644
index 0000000000000..0fe7ca1482f6b
--- /dev/null
+++ b/vendor/magento/framework/GraphQl/Query/QueryParser.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+declare(strict_types=1);
+
+namespace Magento\Framework\GraphQl\Query;
+
+use GraphQL\Language\AST\DocumentNode;
+use GraphQL\Language\Parser;
+use GraphQL\Language\Source;
+
+/**
+ * Wrapper for GraphQl query parser. It parses query string into a `GraphQL\Language\AST\DocumentNode`
+ */
+class QueryParser
+{
+    /**
+     * @var string[]
+     */
+    private $parsedQueries = [];
+
+    /**
+     * Parse query string into a `GraphQL\Language\AST\DocumentNode`.
+     *
+     * @param string $query
+     * @return DocumentNode
+     * @throws \GraphQL\Error\SyntaxError
+     */
+    public function parse(string $query): DocumentNode
+    {
+        $cacheKey = sha1($query);
+        if (!isset($this->parsedQueries[$cacheKey])) {
+            $this->parsedQueries[$cacheKey] = Parser::parse(new Source($query, 'GraphQL'));
+        }
+        return $this->parsedQueries[$cacheKey];
+    }
+}
diff --git a/vendor/magento/framework/GraphQl/Query/QueryProcessor.php b/vendor/magento/framework/GraphQl/Query/QueryProcessor.php
index 03265548300ac..460ed71e7f729 100644
--- a/vendor/magento/framework/GraphQl/Query/QueryProcessor.php
+++ b/vendor/magento/framework/GraphQl/Query/QueryProcessor.php
@@ -9,6 +9,8 @@
 
 use GraphQL\Error\DebugFlag;
 use GraphQL\GraphQL;
+use GraphQL\Language\AST\DocumentNode;
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\GraphQl\Exception\ExceptionFormatter;
 use Magento\Framework\GraphQl\Exception\GraphQlInputException;
 use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
@@ -16,6 +18,7 @@
 
 /**
  * Wrapper for GraphQl execution of a schema
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class QueryProcessor
 {
@@ -34,27 +37,35 @@ class QueryProcessor
      */
     private $errorHandler;
 
+    /**
+     * @var QueryParser
+     */
+    private $queryParser;
+
     /**
      * @param ExceptionFormatter $exceptionFormatter
      * @param QueryComplexityLimiter $queryComplexityLimiter
      * @param ErrorHandlerInterface $errorHandler
+     * @param QueryParser|null $queryParser
      * @SuppressWarnings(PHPMD.LongVariable)
      */
     public function __construct(
         ExceptionFormatter $exceptionFormatter,
         QueryComplexityLimiter $queryComplexityLimiter,
-        ErrorHandlerInterface $errorHandler
+        ErrorHandlerInterface $errorHandler,
+        QueryParser $queryParser = null
     ) {
         $this->exceptionFormatter = $exceptionFormatter;
         $this->queryComplexityLimiter = $queryComplexityLimiter;
         $this->errorHandler = $errorHandler;
+        $this->queryParser = $queryParser ?: ObjectManager::getInstance()->get(QueryParser::class);
     }
 
     /**
      * Process a GraphQl query according to defined schema
      *
      * @param Schema $schema
-     * @param string $source
+     * @param DocumentNode|string $source
      * @param ContextInterface|null $contextValue
      * @param array|null $variableValues
      * @param string|null $operationName
@@ -63,11 +74,14 @@ public function __construct(
      */
     public function process(
         Schema $schema,
-        string $source,
+        DocumentNode|string $source,
         ContextInterface $contextValue = null,
         array $variableValues = null,
         string $operationName = null
     ): array {
+        if (is_string($source)) {
+            $source = $this->queryParser->parse($source);
+        }
         if (!$this->exceptionFormatter->shouldShowDetail()) {
             $this->queryComplexityLimiter->validateAliasCount($source);
             $this->queryComplexityLimiter->validateFieldCount($source);
