Query DynamoDB sur un GSI en Node.js (AWS SDK v3)

Une requête sur GSI est une Query ordinaire plus IndexName, et deux choses cessent alors de se comporter comme une requête sur table : le flag de cohérence dont tu as l'habitude devient une erreur, et le curseur de pagination gagne un attribut supplémentaire. Ici, AlbumTitle-index récupère les morceaux par album, ce que la table de base (Artist + SongTitle) ne peut pas faire sans un scan.

Code

import {DynamoDBClient, QueryCommand} from '@aws-sdk/client-dynamodb';

const client = new DynamoDBClient({region: 'us-east-1'});

const items = [];
let lastEvaluatedKey;

do {
  const response = await client.send(
    new QueryCommand({
      TableName: 'Music',
      IndexName: 'AlbumTitle-index',
      KeyConditionExpression: '#hashKey = :hashKeyValue',
      ExpressionAttributeNames: {
        '#hashKey': 'AlbumTitle'
      },
      ExpressionAttributeValues: {
        ':hashKeyValue': {S: 'Danzon'}
      },
      ExclusiveStartKey: lastEvaluatedKey
    })
  );

  items.push(...(response.Items ?? []));
  lastEvaluatedKey = response.LastEvaluatedKey;
} while (lastEvaluatedKey);

console.log(`Found ${items.length} songs on the album`);

Le curseur fait trois attributs de large, pas deux

Lance la boucle ci-dessus sur 300 morceaux d'un même album et regarde le LastEvaluatedKey qu'elle te rend :

table query  -> ['Artist', 'SongTitle']
GSI query    -> ['AlbumTitle', 'Artist', 'SongTitle']

Une clé de GSI n'est pas unique : la clé d'index seule ne peut donc pas reprendre un parcours. DynamoDB renvoie la clé d'index et la clé de la table de base ensemble, et les deux doivent repartir intactes dans ExclusiveStartKey. C'est pour ça qu'un curseur bricolé à la main qui stocke « la dernière clé de tri vue » fonctionne sur une table et perd ou répète silencieusement des éléments sur un index — et pourquoi persister cette clé côté client est une mauvaise idée quand la clé de table est un identifiant utilisateur que tu préférerais ne pas divulguer.

ConsistentRead: true est un 400, pas une amélioration

L'intuition, c'est qu'une lecture fortement cohérente coûte plus de capacité et te donne des données plus fraîches. Sur un GSI, elle te coûte la requête :

ValidationException: Consistent reads are not supported on global secondary indexes
HTTP 400

La référence de l'API est tout aussi tranchée : "Strongly consistent reads are not supported on global secondary indexes. If you query a global secondary index with ConsistentRead set to true, you will receive a ValidationException." Scan sur un GSI rejette le même flag avec le même message. Les index secondaires locaux, eux, l'acceptent.

Reproduit le 2026-07-28 sur DynamoDB Local (amazon/dynamodb-local) avec @aws-sdk/client-dynamodb 3.1095.0 sur node v24.18.0. Le texte d'erreur et la forme des clés sont la sortie du moteur lui-même.

Explication

  • IndexName ne remplace pas TableName. Les deux vont dans la même commande, et la KeyConditionExpression nomme alors la clé de partition de l'index (AlbumTitle), pas celle de la table, avec le même jeu d'opérateurs qu'une requête sur table.
  • Tu obtiens la projection et rien d'autre. Une requête sur GSI renvoie ce que l'index projette (ALL, KEYS_ONLY ou la liste INCLUDE), et selon la référence de l'API "global secondary index queries cannot fetch attributes from the parent table". Un attribut manquant signifie un GetItem de suivi sur la clé de base, ou une projection plus large et un index recréé.
  • Les éléments dépourvus de la clé d'index n'apparaissent jamais. C'est le motif de l'index creux, et c'est une fonctionnalité : indexe uniquement les lignes avec status = "OPEN" et le GSI reste petit. C'est aussi la raison pour laquelle une requête sur GSI peut renvoyer moins d'éléments que prévu sans lever la moindre erreur.
  • La réplication est asynchrone, donc une écriture qui vient d'atterrir sur la table peut ne pas encore être dans l'index. Prévois-le dans les chemins lecture-après-écriture plutôt que de réessayer en boucle serrée.

Le faire visuellement

Ajouter un GSI après coup est la façon coûteuse d'apprendre tout ça. Le planificateur de single-table design prend tes modèles d'accès et détermine lesquels ont besoin d'une clé d'index et lesquels la table de base sert déjà.

Pour parcourir les index d'une table et lancer des requêtes GSI depuis un formulaire, avec une grille paginée, télécharge DynoTable.

Exemples liés

Références

Construis cette requête visuellement

Compose cette opération dans le Générateur de requêtes DynamoDB gratuit — condition de clé, filtre, index, Limit, ordre de tri et boucle de pagination — et copie-la en retour comme programme exécutable SDK v3, CLI ou boto3.

Ouvrir le Générateur de requêtes DynamoDB

Travaille avec DynamoDB sans la Console

Un client de bureau rapide pour DynamoDB qui exécute le vrai SQL que DynamoDB ne peut pas — JOINs, GROUP BY, agrégations — avec édition visuelle et un agent IA sur tes propres clés Bedrock.

Essai gratuit de 30 jours, sans carte bancaire — ensuite la formule Gratuit, sans limite de durée.