diff --git a/src/Plugin/GraphQL/DataProducer/EntityLoadRevision.php b/src/Plugin/GraphQL/DataProducer/EntityLoadRevision.php
new file mode 100644
index 0000000000000000000000000000000000000000..ec3c930ffa02957ac3a5cdaf3c2e634f644eb9c3
--- /dev/null
+++ b/src/Plugin/GraphQL/DataProducer/EntityLoadRevision.php
@@ -0,0 +1,196 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\graphql_compose\Plugin\GraphQL\DataProducer;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\RevisionableInterface;
+use Drupal\Core\Entity\TranslatableInterface;
+use Drupal\Core\Entity\TranslatableRevisionableStorageInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\graphql\GraphQL\Buffers\EntityRevisionBuffer;
+use Drupal\graphql\GraphQL\Execution\FieldContext;
+use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
+use GraphQL\Deferred;
+use GraphQL\Error\UserError;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Loads the entity by revision.
+ *
+ * @DataProducer(
+ *   id = "entity_load_revision",
+ *   name = @Translation("Load entity revision"),
+ *   description = @Translation("The entity belonging to the current url."),
+ *   produces = @ContextDefinition("entity",
+ *     label = @Translation("Entity"),
+ *   ),
+ *   consumes = {
+ *     "entity" = @ContextDefinition("any",
+ *       label = @Translation("The entity to load revisions from"),
+ *     ),
+ *     "identifier" = @ContextDefinition("any",
+ *       label = @Translation("Revision ID"),
+ *       required = FALSE,
+ *     ),
+ *     "language" = @ContextDefinition("string",
+ *       label = @Translation("Language code"),
+ *       required = FALSE,
+ *     ),
+ *   },
+ * )
+ */
+class EntityLoadRevision extends DataProducerPluginBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The latest revision identifiers.
+   */
+  const REVISION_LATEST = [
+    'latest',
+    'newest',
+    'working',
+    'working-copy',
+  ];
+
+  /**
+   * The current revision identifiers.
+   */
+  const REVISION_CURRENT = [
+    'active',
+    'current',
+    'default',
+  ];
+
+  /**
+   * Constructs a \Drupal\Component\Plugin\PluginBase object.
+   *
+   * @param array $configuration
+   *   The plugin configuration array.
+   * @param string $pluginId
+   *   The plugin id.
+   * @param mixed $pluginDefinition
+   *   The plugin definition array.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
+   *   The language manager service.
+   * @param \Drupal\graphql\GraphQL\Buffers\EntityRevisionBuffer $entityRevisionBuffer
+   *   The entity revision buffer service.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $languageManager
+   *   The language manager service.
+   */
+  public function __construct(
+    array $configuration,
+    $pluginId,
+    $pluginDefinition,
+    protected EntityTypeManagerInterface $entityTypeManager,
+    protected EntityRevisionBuffer $entityRevisionBuffer,
+    protected LanguageManagerInterface $languageManager,
+  ) {
+    parent::__construct($configuration, $pluginId, $pluginDefinition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager'),
+      $container->get('graphql.buffer.entity_revision'),
+      $container->get('language_manager'),
+    );
+  }
+
+  /**
+   * Resolve the entity revision.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface|null $entity
+   *   The entity to load revisions from.
+   * @param int|string|null $identifier
+   *   The revision ID to load.
+   * @param string|null $language
+   *   The language code to use.
+   * @param \Drupal\graphql\GraphQL\Execution\FieldContext $context
+   *   Cache context.
+   */
+  public function resolve(?EntityInterface $entity, int|string|null $identifier, ?string $language, FieldContext $context): Deferred|EntityInterface|null {
+
+    $identifier = $identifier ? strtolower((string) $identifier) : NULL;
+
+    if (!$identifier || in_array($identifier, self::REVISION_CURRENT)) {
+      return $entity;
+    }
+
+    if (!$entity instanceof RevisionableInterface) {
+      return $entity;
+    };
+
+    $entity_id = $entity->id();
+    $entity_type_id = $entity->getEntityTypeId();
+
+    // We need a langcode for getLatestTranslationAffectedRevisionId().
+    // Set the default langcode to the current context language.
+    // Fall back to the current language.
+    $langcode = $language
+      ?: $context->getContextLanguage()
+      ?: $this->languageManager->getCurrentLanguage()->getId();
+
+    // Quickly resolve the latest revision.
+    if (in_array($identifier, self::REVISION_LATEST)) {
+      /** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */
+      $storage = $this->entityTypeManager->getStorage($entity_type_id);
+
+      $identifier = ($storage instanceof TranslatableRevisionableStorageInterface)
+        ? $storage->getLatestTranslationAffectedRevisionId($entity_id, $langcode)
+        : $storage->getLatestRevisionId($entity_id);
+
+      // Did not get a valid revision identifier.
+      if (!$identifier) {
+        return NULL;
+      }
+    }
+
+    // Add the entity to the buffer.
+    $resolver = $this->entityRevisionBuffer->add($entity_type_id, $identifier);
+
+    return new Deferred(function () use ($resolver, $langcode, $entity_id, $entity_type_id, $context) {
+
+      /** @var \Drupal\Core\Entity\RevisionableInterface|null $revision */
+      if (!$revision = $resolver()) {
+        // Add cache list tags to invalidate the cache.
+        $entity_type = $this->entityTypeManager->getDefinition($entity_type_id, FALSE);
+        if ($entity_type) {
+          $context->addCacheTags($entity_type->getListCacheTags());
+        }
+
+        $context->addCacheTags(['4xx-response']);
+        return NULL;
+      }
+
+      // Check the revision belongs to the entity.
+      if ($revision->id() !== $entity_id) {
+        throw new UserError('The requested revision does not belong to the requested entity.');
+      }
+
+      $context->setContextValue('revision', $revision->getRevisionId());
+
+      // A specific language was requested.
+      // Ensure the revision is translated.
+      if ($langcode && $revision instanceof TranslatableInterface && $revision->hasTranslation($langcode) && $langcode !== $revision->language()->getId()) {
+        $revision = $revision->getTranslation($langcode);
+        $revision->addCacheContexts(["static:language:{$langcode}"]);
+      }
+
+      // Check revision access.
+      $access = $revision->access('view', NULL, TRUE);
+      $context->addCacheableDependency($access);
+
+      return $access->isAllowed() ? $revision : NULL;
+    });
+  }
+
+}
diff --git a/src/Plugin/GraphQLCompose/GraphQLComposeEntityTypeBase.php b/src/Plugin/GraphQLCompose/GraphQLComposeEntityTypeBase.php
index 877b2d60c7dcfc632fb3b4b850028025ca3bbbf6..fc9d0b7373912786e16f36372528aa5c97d540d5 100644
--- a/src/Plugin/GraphQLCompose/GraphQLComposeEntityTypeBase.php
+++ b/src/Plugin/GraphQLCompose/GraphQLComposeEntityTypeBase.php
@@ -253,18 +253,26 @@ abstract class GraphQLComposeEntityTypeBase extends PluginBase implements GraphQ
    * Register unions and interfaces only if there is multiple enabled bundles.
    */
   public function registerTypes(): void {
-
     $bundles = $this->getBundles();
     if (!$bundles) {
       return;
     }
 
-    // Register a bundle types into the schema.
+    $this->registerEntityInterface();
+    $this->registerEntityUnion();
+    $this->registerEntityQuery();
+
     foreach ($bundles as $bundle) {
       $this->registerBundleTypes($bundle);
+      $this->registerBundleQueries($bundle);
+      $this->registerBundleFieldUnions($bundle);
     }
+  }
 
-    // Create generic entity wide interface.
+  /**
+   * Register a generic entity wide interface.
+   */
+  protected function registerEntityInterface(): void {
     $interface_fields = $this->gqlFieldTypeManager->getInterfaceFields($this->getEntityTypeId());
     if ($interface_fields) {
       $interface = new InterfaceType([
@@ -289,11 +297,15 @@ abstract class GraphQLComposeEntityTypeBase extends PluginBase implements GraphQ
 
       $this->gqlSchemaTypeManager->add($interface);
     }
+  }
 
-    // Create generic entity wide union.
+  /**
+   * Register a generic entity wide union.
+   */
+  protected function registerEntityUnion(): void {
     $union_types = array_map(
       fn(EntityTypeWrapper $bundle): string => $bundle->getTypeSdl(),
-      $bundles
+      $this->getBundles()
     );
 
     $entity_union = new UnionType([
@@ -306,10 +318,14 @@ abstract class GraphQLComposeEntityTypeBase extends PluginBase implements GraphQ
     ]);
 
     $this->gqlSchemaTypeManager->add($entity_union);
+  }
 
-    // Create generic entity wide query.
+  /**
+   * Register a generic entity wide query.
+   */
+  protected function registerEntityQuery(): void {
     $enabled_query_bundles = array_filter(
-      $bundles,
+      $this->getBundles(),
       fn(EntityTypeWrapper $bundle) => $bundle->isQueryLoadEnabled()
     );
 
@@ -339,6 +355,10 @@ abstract class GraphQLComposeEntityTypeBase extends PluginBase implements GraphQ
                 'type' => Type::string(),
                 'description' => (string) $this->t('Optionally set the response language. Eg en, ja, fr.'),
               ] : [],
+              'revision' => $this->getEntityType()->isRevisionable() ? [
+                'type' => Type::id(),
+                'description' => (string) $this->t('Optionally set the revision of the entity. Eg current, latest, or an ID.'),
+              ] : [],
             ]),
           ],
         ],
@@ -386,8 +406,15 @@ abstract class GraphQLComposeEntityTypeBase extends PluginBase implements GraphQ
     ]);
 
     $this->gqlSchemaTypeManager->add($entityType);
+  }
 
-    // Create bundle query.
+  /**
+   * Register individual bundle queries into the schema.
+   *
+   * @param \Drupal\graphql_compose\Wrapper\EntityTypeWrapper $bundle
+   *   The bundle to register.
+   */
+  protected function registerBundleQueries(EntityTypeWrapper $bundle): void {
     if (!$this->isQueryLoadSimple() && $bundle->isQueryLoadEnabled()) {
       $entityQuery = new ObjectType([
         'name' => 'Query',
@@ -408,6 +435,10 @@ abstract class GraphQLComposeEntityTypeBase extends PluginBase implements GraphQ
                 'type' => Type::string(),
                 'description' => (string) $this->t('Optionally set the response language. Eg en, ja, fr.'),
               ] : [],
+              'revision' => $this->getEntityType()->isRevisionable() ? [
+                'type' => Type::id(),
+                'description' => (string) $this->t('Optionally set the revision of the entity. Eg current, latest, or an ID.'),
+              ] : [],
             ]),
           ],
         ],
@@ -415,6 +446,19 @@ abstract class GraphQLComposeEntityTypeBase extends PluginBase implements GraphQ
 
       $this->gqlSchemaTypeManager->extend($entityQuery);
     }
+  }
+
+  /**
+   * Register a bundle field union types into the schema.
+   *
+   * @param \Drupal\graphql_compose\Wrapper\EntityTypeWrapper $bundle
+   *   The bundle to register.
+   */
+  protected function registerBundleFieldUnions(EntityTypeWrapper $bundle): void {
+    $fields = $this->gqlFieldTypeManager->getBundleFields(
+      $this->getEntityTypeId(),
+      $bundle->getEntity()->id()
+    );
 
     // Add per-field union types.
     foreach ($fields as $field_plugin) {
@@ -456,23 +500,33 @@ abstract class GraphQLComposeEntityTypeBase extends PluginBase implements GraphQ
    * Resolve unions only if there is multiple enabled bundles.
    */
   public function registerResolvers(ResolverRegistryInterface $registry, ResolverBuilder $builder): void {
-
     $bundles = $this->getBundles();
     if (!$bundles) {
       return;
     }
 
-    $entity_class = $this->entityTypeManager
-      ->getDefinition($this->getEntityTypeId())
-      ->getClass();
+    $this->resolveEntityQuery($registry, $builder);
+    $this->resolveEntityUnion($registry, $builder);
 
     foreach ($bundles as $bundle) {
-      $this->registerBundleResolvers($registry, $builder, $bundle, $entity_class);
+      $this->resolveBundleTypes($registry, $builder, $bundle);
+      $this->resolveBundleQueries($registry, $builder, $bundle);
+      $this->resolveBundleFieldUnions($registry, $builder, $bundle);
     }
+  }
 
+  /**
+   * Resolve generic entity query.
+   *
+   * @param \Drupal\graphql\GraphQL\ResolverRegistryInterface $registry
+   *   The resolver registry.
+   * @param \Drupal\graphql\GraphQL\ResolverBuilder $builder
+   *   The resolver builder.
+   */
+  protected function resolveEntityQuery(ResolverRegistryInterface $registry, ResolverBuilder $builder): void {
     // Resolve generic load by id query.
     $enabled_query_bundles = array_filter(
-      $bundles,
+      $this->getBundles(),
       fn(EntityTypeWrapper $bundle) => $bundle->isQueryLoadEnabled()
     );
 
@@ -486,31 +540,51 @@ abstract class GraphQLComposeEntityTypeBase extends PluginBase implements GraphQ
       $registry->addFieldResolver(
         'Query',
         $this->getNameSdl(),
-        $builder->produce('entity_load_by_uuid_or_id')
-          ->map('type', $builder->fromValue($this->getEntityTypeId()))
-          ->map('bundles', $builder->fromValue($enabled_query_bundle_ids))
-          ->map('identifier', $builder->fromArgument('id'))
-          ->map('language', $builder->fromArgument('langcode'))
+        $builder->compose(
+          $builder->produce('entity_load_by_uuid_or_id')
+            ->map('type', $builder->fromValue($this->getEntityTypeId()))
+            ->map('bundles', $builder->fromValue($enabled_query_bundle_ids))
+            ->map('identifier', $builder->fromArgument('id'))
+            ->map('language', $builder->fromArgument('langcode')),
+          $builder->produce('entity_load_revision')
+            ->map('entity', $builder->fromParent())
+            ->map('identifier', $builder->fromArgument('revision'))
+            ->map('language', $builder->fromArgument('langcode'))
+        )
       );
     }
+  }
+
+  /**
+   * Resolve generic entity wide union.
+   *
+   * @param \Drupal\graphql\GraphQL\ResolverRegistryInterface $registry
+   *   The resolver registry.
+   * @param \Drupal\graphql\GraphQL\ResolverBuilder $builder
+   *   The resolver builder.
+   */
+  protected function resolveEntityUnion(ResolverRegistryInterface $registry, ResolverBuilder $builder): void {
+    // The expected class for the entity type.
+    $class = $this->entityTypeManager
+      ->getDefinition($this->getEntityTypeId())
+      ->getClass();
 
     // Resolve generic entity wide union.
     $registry->addTypeResolver(
       $this->getUnionTypeSdl(),
-      function ($value) use ($entity_class) {
-        if (!is_a($value, $entity_class, TRUE)) {
-          throw new UserError(sprintf('Could not resolve union entity type %s', $entity_class));
+      function ($value) use ($class) {
+        if (!is_a($value, $class, TRUE)) {
+          throw new UserError(sprintf('Could not resolve union entity type %s', $class));
         }
 
         $bundle = $this->getBundle($value->bundle());
         if (!$bundle) {
-          throw new UserError(sprintf('Could not resolve union entity bundle %s::%s, is it enabled?', $entity_class, $value->bundle()));
+          throw new UserError(sprintf('Could not resolve union entity bundle %s::%s, is it enabled?', $class, $value->bundle()));
         }
 
         return $bundle->getTypeSdl();
       }
     );
-
   }
 
   /**
@@ -521,28 +595,31 @@ abstract class GraphQLComposeEntityTypeBase extends PluginBase implements GraphQ
    * @param \Drupal\graphql\GraphQL\ResolverBuilder $builder
    *   The resolver builder.
    * @param \Drupal\graphql_compose\Wrapper\EntityTypeWrapper $bundle
-   *   The bundle to register.
-   * @param string $entity_class
-   *   The bundle's base entity class.
+   *   The bundle to resolve.
    */
-  protected function registerBundleResolvers(ResolverRegistryInterface $registry, ResolverBuilder $builder, EntityTypeWrapper $bundle, string $entity_class): void {
-    // Add bundle type resolution.
+  protected function resolveBundleTypes(ResolverRegistryInterface $registry, ResolverBuilder $builder, EntityTypeWrapper $bundle): void {
+
+    // The expected class for the entity type.
+    $class = $this->entityTypeManager
+      ->getDefinition($this->getEntityTypeId())
+      ->getClass();
+
     $registry->addTypeResolver(
       $bundle->getTypeSdl(),
-      function ($value) use ($entity_class) {
-        if (!is_a($value, $entity_class, TRUE)) {
-          throw new UserError(sprintf('Could not resolve entity type %s', $entity_class));
+      function ($value) use ($class) {
+        if (!is_a($value, $class, TRUE)) {
+          throw new UserError(sprintf('Could not resolve entity type %s', $class));
         }
         return $this->getBundle($value->bundle())->getTypeSdl();
       }
     );
 
+    // Add fields to bundle type.
     $fields = $this->gqlFieldTypeManager->getBundleFields(
       $this->getEntityTypeId(),
       $bundle->getEntity()->id()
     );
 
-    // Add fields to bundle type.
     foreach ($fields as $field_plugin) {
       $registry->addFieldResolver(
         $bundle->getTypeSdl(),
@@ -553,19 +630,55 @@ abstract class GraphQLComposeEntityTypeBase extends PluginBase implements GraphQ
           ->map('value', $field_plugin->getProducers($builder)),
       );
     }
+  }
 
-    // Enable loading by query with id.
+  /**
+   * Resolve bundle queries for the schema.
+   *
+   * @param \Drupal\graphql\GraphQL\ResolverRegistryInterface $registry
+   *   The resolver registry.
+   * @param \Drupal\graphql\GraphQL\ResolverBuilder $builder
+   *   The resolver builder.
+   * @param \Drupal\graphql_compose\Wrapper\EntityTypeWrapper $bundle
+   *   The bundle to resolve.
+   */
+  protected function resolveBundleQueries(ResolverRegistryInterface $registry, ResolverBuilder $builder, EntityTypeWrapper $bundle): void {
     if (!$this->isQueryLoadSimple() && $bundle->isQueryLoadEnabled()) {
       $registry->addFieldResolver(
         'Query',
         $bundle->getNameSdl(),
-        $builder->produce('entity_load_by_uuid_or_id')
-          ->map('type', $builder->fromValue($this->getEntityTypeId()))
-          ->map('bundles', $builder->fromValue([$bundle->getEntity()->id()]))
-          ->map('identifier', $builder->fromArgument('id'))
-          ->map('language', $builder->fromArgument('langcode'))
+        $builder->compose(
+          $builder->produce('entity_load_by_uuid_or_id')
+            ->map('type', $builder->fromValue($this->getEntityTypeId()))
+            ->map('bundles', $builder->fromValue([$bundle->getEntity()->id()]))
+            ->map('identifier', $builder->fromArgument('id'))
+            ->map('language', $builder->fromArgument('langcode')),
+          $builder->produce('entity_load_revision')
+            ->map('entity', $builder->fromParent())
+            ->map('identifier', $builder->fromArgument('revision'))
+            ->map('language', $builder->fromArgument('langcode'))
+        )
       );
     }
+  }
+
+  /**
+   * Resolve bundle field unions for the schema.
+   *
+   * @param \Drupal\graphql\GraphQL\ResolverRegistryInterface $registry
+   *   The resolver registry.
+   * @param \Drupal\graphql\GraphQL\ResolverBuilder $builder
+   *   The resolver builder.
+   * @param \Drupal\graphql_compose\Wrapper\EntityTypeWrapper $bundle
+   *   The bundle to register.
+   */
+  protected function resolveBundleFieldUnions(ResolverRegistryInterface $registry, ResolverBuilder $builder, EntityTypeWrapper $bundle): void {
+
+    // Get the bundle fields.
+    $fields = $this->gqlFieldTypeManager->getBundleFields(
+      $this->getEntityTypeId(),
+      $bundle->getEntity()->id()
+    );
 
     // Add union field resolution for non-simple unions.
     foreach ($fields as $field_plugin) {
diff --git a/tests/src/Functional/Core/EntityRevisionTest.php b/tests/src/Functional/Core/EntityRevisionTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..c4f4b79ebe3975b0a7595874779051863cb0aee3
--- /dev/null
+++ b/tests/src/Functional/Core/EntityRevisionTest.php
@@ -0,0 +1,461 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\graphql_compose\Functional\Core;
+
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\language\Entity\ContentLanguageSettings;
+use Drupal\node\Entity\Node;
+use Drupal\node\NodeInterface;
+use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
+use Drupal\Tests\graphql_compose\Functional\GraphQLComposeBrowserTestBase;
+use Drupal\user\UserInterface;
+
+/**
+ * Test the entity version is loading as expected.
+ */
+class EntityRevisionTest extends GraphQLComposeBrowserTestBase {
+  use ContentModerationTestTrait;
+
+  /**
+   * The node.
+   *
+   * @var \Drupal\node\NodeInterface
+   */
+  protected NodeInterface $node;
+
+  /**
+   * The privileged user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected UserInterface $privilegedUser;
+
+  /**
+   * The revision ids.
+   *
+   * @var array
+   */
+  protected array $revisionIds = [];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'workflows',
+    'content_moderation',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+
+    $this->createContentType([
+      'type' => 'test',
+      'name' => 'Node test type',
+    ]);
+
+    $workflow = $this->createEditorialWorkflow();
+    $this->addEntityTypeAndBundleToWorkflow($workflow, 'node', 'test');
+
+    // Create the initial revision.
+    $this->node = $this->createNode([
+      'type' => 'test',
+      'title' => 'Test',
+      'status' => 1,
+      'moderation_state' => 'published',
+    ]);
+
+    // Store some revisions for testing loading by id.
+    $this->revisionIds['A'] = $this->createNodeDraft($this->node, ['title' => 'Test A']);
+    $this->revisionIds['B'] = $this->createNodeDraft($this->node, ['title' => 'Test B']);
+    $this->revisionIds['C'] = $this->createNodeDraft($this->node, ['title' => 'Test C']);
+
+    // Create the latest working english copy.
+    $this->createNodeDraft($this->node, ['title' => 'Test working-copy']);
+
+    $this->setEntityConfig('node', 'test', [
+      'enabled' => TRUE,
+      'query_load_enabled' => TRUE,
+    ]);
+
+    // Create a user with the view all permissions.
+    $this->privilegedUser = $this->createUser([
+      'access content',
+      'view any unpublished content',
+      'view latest version',
+      ...$this->graphqlPermissions,
+    ]);
+
+    node_access_rebuild();
+  }
+
+  /**
+   * Install the language modules and create some translations.
+   */
+  private function setupLanguageModules(): void {
+    $modules = [
+      'content_translation',
+      'config_translation',
+      'language',
+    ];
+
+    $this->container->get('module_installer')->install($modules, TRUE);
+
+    $this->resetAll();
+
+    ConfigurableLanguage::createFromLangcode('ja')->save();
+    ConfigurableLanguage::createFromLangcode('de')->save();
+    ConfigurableLanguage::createFromLangcode('abc')->save();
+
+    // Enable translations for the test node type.
+    ContentLanguageSettings::loadByEntityTypeBundle('node', 'test')
+      ->setDefaultLangcode(LanguageInterface::LANGCODE_SITE_DEFAULT)
+      ->setLanguageAlterable(TRUE)
+      ->save();
+
+    $this->node = Node::load($this->node->id());
+
+    // Enable translations for the test node type.
+    $this->node->addTranslation('ja', [
+      'title' => 'Test (JA)',
+    ])->save();
+
+    $this->node->addTranslation('de', [
+      'title' => 'Test (DE)',
+    ])->save();
+
+    // Create some translation drafts.
+    $this->createNodeDraft($this->node->getTranslation('ja'), ['title' => 'Test working-copy (JA)']);
+
+    node_access_rebuild();
+  }
+
+  /**
+   * Creates a new node revision.
+   *
+   * @param \Drupal\node\NodeInterface $node
+   *   The node.
+   * @param array $values
+   *   The field values.
+   *
+   * @return int|string|null
+   *   The revision id.
+   */
+  private function createNodeDraft(NodeInterface $node, array $values): int|string|null {
+    foreach ($values as $field_name => $value) {
+      $node->set($field_name, $value);
+    }
+    $node->set('moderation_state', 'draft');
+    $node->save();
+
+    return $node->getRevisionId();
+  }
+
+  /**
+   * Test load latest version of entity.
+   */
+  public function testNodeLoadByLatest(): void {
+    $query = <<<GQL
+      query {
+        node(id: "{$this->node->uuid()}", revision: "current") {
+          ... on NodeInterface {
+            id
+            title
+          }
+        }
+      }
+    GQL;
+
+    $content = $this->executeQuery($query);
+    $this->assertEquals('Test', $content['data']['node']['title']);
+
+    // Enable languages and re-test.
+    $this->setupLanguageModules();
+
+    $content = $this->executeQuery($query);
+    $this->assertEquals('Test', $content['data']['node']['title']);
+
+    // Login as user with view all revision permission.
+    $this->drupalLogin($this->privilegedUser);
+
+    $content = $this->executeQuery($query);
+    $this->assertEquals('Test', $content['data']['node']['title']);
+  }
+
+  /**
+   * Test loading revision by numeric id.
+   */
+  public function testNodeNumericRevisions(): void {
+    foreach ($this->revisionIds as $key => $revisionId) {
+      $query = <<<GQL
+        query {
+          node(id: "{$this->node->uuid()}", revision: {$revisionId}) {
+            ... on NodeInterface {
+              id
+              title
+            }
+          }
+        }
+      GQL;
+
+      $content = $this->executeQuery($query);
+
+      // The working copy is not visible to the anonymous user.
+      $this->assertNull($content['data']['node']);
+
+      // Login as user with view all revision permission.
+      $this->drupalLogin($this->privilegedUser);
+
+      $content = $this->executeQuery($query);
+      $this->assertEquals('Test ' . $key, $content['data']['node']['title']);
+
+      $this->drupalLogout();
+    }
+  }
+
+  /**
+   * Tests working copy retrieval.
+   */
+  public function testNodeLoadByWorkingCopy(): void {
+    $query = <<<GQL
+      query {
+        node(id: "{$this->node->uuid()}", revision: "latest") {
+          ... on NodeInterface {
+            id
+            title
+          }
+        }
+      }
+    GQL;
+
+    // The working copy is not visible to the anonymous user.
+    $content = $this->executeQuery($query);
+    $this->assertNull($content['data']['node']);
+
+    // Login as user with view all revision permission.
+    $this->drupalLogin($this->privilegedUser);
+
+    $content = $this->executeQuery($query);
+    $this->assertEquals('Test working-copy', $content['data']['node']['title']);
+
+    // Enable languages and re-test.
+    $this->setupLanguageModules();
+
+    $content = $this->executeQuery($query);
+    $this->assertEquals('Test working-copy', $content['data']['node']['title']);
+  }
+
+  /**
+   * Test load latest version of entity.
+   */
+  public function testNodeLoadNoLangByLatest(): void {
+
+    $this->setupLanguageModules();
+
+    $query = <<<GQL
+      query {
+        node(id: "{$this->node->uuid()}", revision: "current", langcode: "abc") {
+          ... on NodeInterface {
+            id
+            title
+          }
+        }
+      }
+    GQL;
+
+    $content = $this->executeQuery($query);
+    $this->assertNull($content['data']['node']);
+
+    $query = <<<GQL
+      query {
+        node(id: "{$this->node->uuid()}", revision: "latest", langcode: "abc") {
+          ... on NodeInterface {
+            id
+            title
+          }
+        }
+      }
+    GQL;
+
+    $content = $this->executeQuery($query);
+    $this->assertNull($content['data']['node']);
+  }
+
+  /**
+   * Test revision loading works with simple queries disabled.
+   */
+  public function testNodeRevisionLoadNonSimpleQueries(): void {
+    $this->setConfig('settings.simple_queries', FALSE);
+
+    $query_current = <<<GQL
+      query {
+        nodeTest(id: "{$this->node->uuid()}", revision: "current") {
+          id
+          title
+        }
+      }
+    GQL;
+
+    $query_latest = <<<GQL
+      query {
+        nodeTest(id: "{$this->node->uuid()}", revision: "latest") {
+          id
+          title
+        }
+      }
+    GQL;
+
+    $content = $this->executeQuery($query_current);
+    $this->assertEquals('Test', $content['data']['nodeTest']['title']);
+
+    // Login as user with view all revision permission.
+    $this->drupalLogin($this->privilegedUser);
+
+    $content = $this->executeQuery($query_latest);
+    $this->assertEquals('Test working-copy', $content['data']['nodeTest']['title']);
+  }
+
+  /**
+   * Test incorrect revision id returns null.
+   */
+  public function testNodeIncorrectRevisionIdReturnsNull(): void {
+    $query = <<<GQL
+      query {
+        node(id: "{$this->node->uuid()}", revision: "90210") {
+          ... on NodeInterface {
+            id
+            title
+          }
+        }
+      }
+    GQL;
+
+    $content = $this->executeQuery($query);
+    $this->assertNull($content['data']['node']);
+  }
+
+  /**
+   * Test incorrect revision id returns null.
+   */
+  public function testNodeWrongRevisionIdReturnsNull(): void {
+
+    // Login as user with view all revision permission.
+    $this->drupalLogin($this->privilegedUser);
+
+    $new_node = $this->createNode([
+      'type' => 'test',
+      'title' => 'Bonk',
+      'status' => 1,
+      'moderation_state' => 'published',
+    ]);
+
+    $new_revision_id = $this->createNodeDraft($new_node, ['title' => 'Bunk']);
+
+    // Use the wrong (valid) revision id on the node.
+    $query = <<<GQL
+      query {
+        node(id: "{$this->node->uuid()}", revision: {$new_revision_id}) {
+          ... on NodeInterface {
+            id
+            title
+          }
+        }
+      }
+    GQL;
+
+    // Should be an error.
+    $content = $this->executeQuery($query);
+    $this->assertSame('The requested revision does not belong to the requested entity.', $content['errors'][0]['message']);
+    $this->assertNull($content['data']['node']);
+
+    // Test the new node.
+    $query = <<<GQL
+      query {
+        node(id: "{$new_node->uuid()}", revision: {$new_revision_id}) {
+          ... on NodeInterface {
+            id
+            title
+          }
+        }
+      }
+    GQL;
+
+    $content = $this->executeQuery($query);
+    $this->assertEquals('Bunk', $content['data']['node']['title']);
+  }
+
+  /**
+   * Test latest version retrieval for translated content.
+   */
+  public function testNodeLoadByLatestWithLangcode(): void {
+
+    $this->setupLanguageModules();
+
+    $query = <<<GQL
+      query {
+        node(id: "{$this->node->uuid()}", langcode: "ja", revision: "current") {
+          ... on NodeInterface {
+            id
+            title
+          }
+        }
+      }
+    GQL;
+
+    $content = $this->executeQuery($query);
+    $this->assertEquals('Test (JA)', $content['data']['node']['title']);
+  }
+
+  /**
+   * Test working-copy version retrieval for translated content.
+   */
+  public function testNodeLoadByWorkingCopyWithLangcode(): void {
+
+    $this->setupLanguageModules();
+
+    $query = <<<GQL
+      query {
+        node(id: "{$this->node->uuid()}", langcode: "ja", revision: "latest") {
+          ... on NodeInterface {
+            id
+            title
+          }
+        }
+      }
+    GQL;
+
+    // Now try as user with view all revision permission.
+    $this->drupalLogin($this->privilegedUser);
+
+    $content = $this->executeQuery($query);
+    $this->assertEquals('Test working-copy (JA)', $content['data']['node']['title']);
+  }
+
+  /**
+   * Test the latest translated revision is returned when no working-copy.
+   */
+  public function testNodeLoadByWorkingCopyWithLangcodeFallback(): void {
+
+    $this->setupLanguageModules();
+
+    $query = <<<GQL
+      query {
+        node(id: "{$this->node->uuid()}", langcode: "de", revision: "latest") {
+          ... on NodeInterface {
+            id
+            title
+          }
+        }
+      }
+    GQL;
+
+    $content = $this->executeQuery($query);
+    $this->assertEquals('Test (DE)', $content['data']['node']['title']);
+  }
+
+}