Guide pratique pour utiliser le Stockage Table Azure ou Azure Cosmos DB for Table à partir de Node.js

S’APPLIQUE À : Table

Conseil

Le contenu de cet article s’applique au stockage Table Azure et à Azure Cosmos DB for Table. L’API pour Table est une offre premium de stockage de tables qui propose des tables optimisées pour le débit, la distribution globale et des index secondaires automatiques.

Cet article explique comment créer des tables, stocker vos données et effectuer des opérations CRUD sur celles-ci. Les exemples sont écrits en Node.js.

Créer un compte de service Azure

Vous pouvez travailler avec des tables à l’aide du Stockage Table Azure ou d’Azure Cosmos DB. Pour en savoir plus sur les différences entre les offres de table dans ces deux services, consultez la Vue d’ensemble de l’API pour Table. Vous devrez créer un compte pour le service que vous allez utiliser. Les sections suivantes montrent comment créer un stockage Table Azure et le compte Azure Cosmos DB, mais vous pouvez simplement utiliser l’un d’eux.

Créer un compte de stockage Azure

Le moyen le plus simple de créer un compte de stockage Azure est d’utiliser le portail Azure. Pour plus d’informations, consultez la page Créer un compte de stockage.

Il est également possible de créer un compte de stockage Azure avec Azure PowerShell ou Azure CLI.

Si vous préférez ne pas créer de compte de stockage pour le moment, vous avez la possibilité d’utiliser l’émulateur de stockage Azure pour exécuter et tester votre code dans un environnement local. Pour plus d’informations, consultez Utiliser l’émulateur de stockage Azure pour le développement et le test.

Créer un compte Azure Cosmos DB for Table

Pour obtenir des instructions sur la création d’un compte Azure Cosmos DB for Table, consultez Créer un compte de base de données.

Configuration de votre application pour accéder au Stockage Table

Pour utiliser le Stockage Azure ou Azure Cosmos DB, vous avez besoin du Kit de développement logiciel (SDK) Stockage Tables pour Node.js, qui inclut un ensemble de bibliothèques pratiques communiquant avec les services de stockage REST.

Utilisation de Node Package Manager (NPM) pour installer le package

  1. Utilisez une interface de ligne de commande telle que PowerShell (Windows), Terminal (Mac) ou Bash (Unix) pour accéder au dossier dans lequel vous avez créé votre application.
  2. Tapez ce qui suit dans la fenêtre de commande :
   npm install @azure/data-tables
  1. Vous pouvez exécuter manuellement la commande ls pour vérifier que le dossier node_modules a été créé. Dans ce dossier, recherchez le dossier @azure/data-tables, qui contient les bibliothèques dont vous avez besoin pour accéder aux tables.

Importation du package

Ajoutez le code suivant en haut du fichier server.js dans votre application :

const { TableServiceClient, TableClient, AzureNamedKeyCredential, odata } = require("@azure/data-tables");

Se connecter au service de Table Azure

Vous pouvez vous connecter au compte de stockage Azure ou au compte Azure Cosmos DB for Table. Obtenez la clé partagée ou chaîne de connexion en fonction du type de compte que vous utilisez.

Création du client de service Table à partir d’une clé partagée

Le module de Stockage Azure lit les variables d’environnement AZURE_ACCOUNT and AZURE_ACCESS_KEY et AZURE_TABLES_ENDPOINT pour obtenir les informations nécessaires à la connexion à votre compte Stockage Azure ou à Azure Cosmos DB. Si ces variables d’environnement ne sont pas définies, vous devez spécifier les informations de compte lors de l’appel de TableServiceClient. Par exemple, le code suivant crée un objet TableServiceClient :

const endpoint = "<table-endpoint-uri>";
const credential = new AzureNamedKeyCredential(
  "<account-name>",
  "<account-key>"
);

const tableService = new TableServiceClient(
  endpoint,
  credential
);

Création du client de service Table à partir d’une chaîne de connexion

Pour ajouter une connexion Azure Cosmos DB ou à un compte de stockage, créez un objet TableServiceClient et spécifiez le nom, la clé primaire et le point de terminaison de votre compte. Vous pouvez copier ces valeurs depuis Paramètres>Chaîne de connexion sur le portail Azure pour votre compte Azure Cosmos DB ou compte de stockage. Par exemple :

const tableService = TableServiceClient.fromConnectionString("<connection-string>");

Créer une table

L’appel de createTable crée une table du nom spécifié si elle n’existe pas. Dans l'exemple suivant, la table 'mytable' est créée, si elle n'existe pas déjà :

await tableService.createTable('<table-name>');

Création de la table client

Pour interagir avec une table, vous devez créer un objet TableClient à l’aide des mêmes informations d’identification que celles que vous avez utilisées pour créer le TableServiceClient. Le TableClient nécessite également le nom de la table cible.

const tableClient = new TableClient(
  endpoint,
  '<table-name>',
  credential
);

Ajout d'une entité à une table

Pour ajouter une entité, commencez par créer un objet qui définit les propriétés de l'entité. Toutes les entités doivent contenir une propriété PartitionKey et RowKey, qui sont des identificateurs uniques de l’entité.

  • PartitionKey : détermine la partition dans laquelle l’entité est stockée.
  • RowKey : identifie de façon unique l’entité dans la partition.

PartitionKey et RowKey doivent être des valeurs de chaîne.

Voici un exemple de définition d'une entité. La dueDate est définie en tant que type de Date. L'indication du type est facultative et s'ils ne sont pas spécifiés, les types sont inférés.

const task = {
  partitionKey: "hometasks",
  rowKey: "1",
  description: "take out the trash",
  dueDate: new Date(2015, 6, 20)
};

Notes

Il existe également un champ Timestamp pour chaque enregistrement, qu’Azure définit lors de l’insertion ou la mise à jour d’une entité.

Pour ajouter une entité à votre table, transmettez l’objet entité à la méthode createEntity.

let result = await tableClient.createEntity(task);
    // Entity create

Si l’opération réussit, result contient l’ETag et des informations sur l’opération.

Exemple de réponse :

{ 
  clientRequestId: '94d8e2aa-5e02-47e7-830c-258e050c4c63',
  requestId: '08963b85-1002-001b-6d8c-12ae5d000000',
  version: '2019-02-02',
  date: 2022-01-26T08:12:32.000Z,
  etag: `W/"datetime'2022-01-26T08%3A12%3A33.0180348Z'"`,
  preferenceApplied: 'return-no-content',
  'cache-control': 'no-cache',
  'content-length': '0'
}

Mise à jour d'une entité

Différents modes pour les méthodes updateEntity et upsertEntity

  • Fusion : met à jour une entité en mettant à jour les propriétés de l’entité sans remplacer l’entité existante.
  • Remplacer : met à jour une entité existante en remplaçant la totalité de l’entité.

L’exemple suivant illustre la mise à jour d’une entité en utilisant upsertEntity :

// Entity doesn't exist in table, so calling upsertEntity will simply insert the entity.
let result = await tableClient.upsertEntity(task, "Replace");

Si l’entité à mettre à jour n’existe pas, l’opération de mise à jour échoue. Par conséquent, si vous souhaitez stocker une entité indépendamment de son existence ou non, utilisez upsertEntity.

Le result des opérations de mise à jour réussies contient l’Etag de l’entité mise à jour.

Utilisation des groupes d'entités

Il est parfois intéressant de soumettre un lot d'opérations simultanément pour assurer un traitement atomique par le serveur. Pour ce faire, créez un tableau d’opérations et transmettez-le à la méthode submitTransaction sur TableClient.

L'exemple suivant illustre la soumission par lot de deux entités :

const task1 = {
  partitionKey: "hometasks",
  rowKey: "1",
  description: "Take out the trash",
  dueDate: new Date(2015, 6, 20)
};
const task2 = {
  partitionKey: "hometasks",
  rowKey: "2",
  description: "Wash the dishes",
  dueDate: new Date(2015, 6, 20)
};

const tableActions = [
  ["create", task1],
  ["create", task2]
];

let result = await tableClient.submitTransaction(tableActions);
    // Batch completed

Pour les opérations de traitement par lot réussies, result contient les informations de chaque opération du lot.

Récupération d'une entité par clé

Pour envoyer une entité spécifique d’après la valeur PartitionKey et RowKey, utilisez la méthode getEntity.

let result = await tableClient.getEntity("hometasks", "1")
  .catch((error) => {
    // handle any errors
  });
  // result contains the entity

Après cette opération, result contient l’entité.

Interrogation d’un ensemble d’entités

L’exemple suivant crée une requête qui renvoie les cinq premiers éléments avec une PartitionKey « hometasks » et répertorie toutes les entités dans la table.

const topN = 5;
const partitionKey = "hometasks";

const entities = tableClient.listEntities({
  queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` }
});

let topEntities = [];
const iterator = entities.byPage({ maxPageSize: topN });

for await (const page of iterator) {
  topEntities = page;
  break;
}

// Top entities: 5
console.log(`Top entities: ${topEntities.length}`);

// List all the entities in the table
for await (const entity of entities) {
console.log(entity);
}

Interrogation d'un sous-ensemble de propriétés d'entité

Vous pouvez utiliser une requête de table pour extraire uniquement quelques champs d'une entité. Ceci permet de réduire la consommation de bande passante et peut améliorer les performances des requêtes, notamment pour les entités volumineuses. Utilisez la clause select et transmettez les noms des champs à renvoyer. Par exemple, la requête suivante renvoie uniquement les champs description et dueDate.

const topN = 5;
const partitionKey = "hometasks";

const entities = tableClient.listEntities({
  queryOptions: { filter: odata`PartitionKey eq ${partitionKey}`,
                  select: ["description", "dueDate"]  }
});

let topEntities = [];
const iterator = entities.byPage({ maxPageSize: topN });

for await (const page of iterator) {
  topEntities = page;
  break;
}

Suppression d’une entité

Vous pouvez supprimer une entité en utilisant ses clés de partition et de ligne. Dans cet exemple, l’objet task1 contient les valeurs RowKey et PartitionKey de l’entité à supprimer. L'objet est transmis à la méthode deleteEntity .

const tableClient = new TableClient(
  tablesEndpoint,
  tableName,
  new AzureNamedKeyCredential("<accountName>", "<accountKey>")
);

await tableClient.deleteEntity("hometasks", "1");
    // Entity deleted

Notes

Vous avez intérêt à utiliser les ETag pour supprimer des éléments afin de vous assurer que les éléments n'ont pas été modifiés par un autre processus. Consultez Mise à jour d’une entité pour plus d’informations sur l’utilisation des ETags.

Suppression d’une table

Le code suivant permet de supprimer une table d'un compte de stockage.

await tableClient.deleteTable(mytable);
        // Table deleted

Utiliser des jetons de liaison

Si vous interrogez des tables et que les résultats peuvent être volumineux, recherchez des jetons de liaison. Sans que vous en ayez vraiment conscience, de grandes quantités de données peuvent être disponibles pour votre requête si elle n’est pas en mesure de détecter la présence d’un jeton de liaison.

L’objet résultats renvoyé après l’interrogation des entités définit une propriétécontinuationToken si ce jeton est présent. Vous pouvez ensuite utiliser cette propriété pour exécuter une requête sur l’ensemble des entités de table et de partition.

Pendant l’interrogation, un paramètre continuationToken peut être fourni entre l’instance d’objet de requête et la fonction de rappel :

let iterator = tableClient.listEntities().byPage({ maxPageSize: 2 });
let interestingPage;

const page = await tableClient
   .listEntities()
   .byPage({ maxPageSize: 2, continuationToken: interestingPage })
   .next();

 if (!page.done) {
   for (const entity of page.value) {
     console.log(entity.rowKey);
   }
 }

Utilisation des signatures d'accès partagé

Les signatures d’accès partagé (SAP) sont un moyen sécurisé de fournir un accès précis aux tables sans fournir le nom ni les clés de votre compte de stockage. Elles servent souvent à fournir un accès limité à vos données, par exemple pour autoriser une application mobile à interroger des enregistrements.

Une application approuvée, comme un service cloud, génère une SAP à l’aide de l’élément generateTableSas du TableClient, et la fournit à une application non approuvée ou semi-approuvée, comme une application mobile. La signature d'accès partagé est générée à l'aide d'une stratégie, qui décrit les dates de début et de fin de validité de la signature, et le niveau d'accès accordé au détenteur de la signature.

L’exemple suivant génère une nouvelle stratégie d’accès partagé qui autorise le détenteur de la signature d’accès partagé à interroger la table (« r »).

const tablePermissions = {
    query: true
// Allows querying entities
};

// Create the table SAS token
const tableSAS = generateTableSas('mytable', cred, {
  expiresOn: new Date("2022-12-12"),
  permissions: tablePermissions
});

L'application cliente utilise les signatures d'accès partagé avec AzureSASCredential pour effectuer les opérations sur la table. L'exemple suivant se connecte à la table et exécute une requête. Consultez l’article Accorder un accès limité aux ressources du Stockage Azure à l’aide de la signature d’accès partagé (SAS) pour connaître le format de la signature d’accès partagé (SAS) à une table.

// Note in the following command, tablesUrl is in the format: `https://<your_storage_account_name>.table.core.windows.net` and the tableSAS is in the format: `sv=2018-03-28&si=saspolicy&tn=mytable&sig=9aCzs76n0E7y5BpEi2GvsSv433BZa22leDOZXX%2BXXIU%3D`;

const tableService = new TableServiceClient(tablesUrl, new AzureSASCredential(tableSAS));
const partitionKey = "hometasks";

const entities = tableService.listTables({
  queryOptions: { filter: odata`PartitionKey eq ${partitionKey}` }
});

Comme la signature d'accès partagé a été générée seulement avec un accès en requête, une erreur est renvoyée si vous tentez d'insérer, de mettre à jour ou de supprimer des entités.

Listes de contrôle d’accès

Vous pouvez également utiliser une liste de contrôle d'accès (ACL) pour définir la stratégie d'accès pour une signature d'accès partagé. Cela est utile si vous voulez autoriser plusieurs clients à accéder à la table tout en fournissant des stratégies d'accès différentes à chaque client.

Une liste de contrôle d'accès est implémentée à l'aide d'un tableau de stratégies d'accès, dans lequel un ID est associé à chaque stratégie. L’exemple suivant définit deux stratégies ; une pour « user1 » et une pour « user2 » :

var sharedAccessPolicy = [{
  id:"user1",
  accessPolicy:{
    permission: "r" ,
    Start: startsOn,
    Expiry: expiresOn,
  }},
  {
  id:"user2",
  accessPolicy:{
    permissions: "a",
    Start: startsOn,
    Expiry: expiresOn,
  }},
]

L’exemple suivant obtient la liste de contrôle d’accès actuelle pour la table hometasks, puis ajoute les nouvelles stratégies à l’aide de setAccessPolicy. Cette approche permet :

tableClient.getAccessPolicy();
tableClient.setAccessPolicy(sharedAccessPolicy);

Après avoir défini la liste de contrôle d'accès, vous pouvez créer une signature d'accès partagé basée sur l'ID pour une stratégie. L'exemple suivant crée une signature d'accès partagé pour « user2 » :

tableSAS = generateTableSas("hometasks",cred,{identifier:'user2'});

Étapes suivantes

Pour plus d'informations, consultez les ressources ci-dessous.