Uso de Azure Table Storage o Azure Cosmos DB for Table desde Node.js

SE APLICA A: Table

Sugerencia

El contenido de este artículo se aplica a Azure Table Storage y Azure Cosmos DB for Table. La API para Table es una oferta prémium para el almacenamiento de tablas que proporciona tablas con rendimiento optimizado, distribución global e índices secundarios automáticos.

En este artículo se muestra cómo crear tablas, almacenar datos y realizar operaciones CRUD en ellos. Los ejemplos están escritos en Node.js.

Creación de una cuenta de servicio de Azure

Puede trabajar con tablas mediante Azure Table Storage o Azure Cosmos DB. Para obtener más información sobre las diferencias entre las ofertas de tablas de estos dos servicios, consulte la introducción a API para Table. Debe crear una cuenta para el servicio que se va a utilizar. En las secciones siguientes se muestra cómo crear las cuentas de Azure Table Storage y Azure Cosmos DB, pero solo puede usar una de ellas.

Creación de una cuenta de Azure Storage

La manera más sencilla de crear una cuenta de almacenamiento de Azure es mediante Azure Portal. Para obtener más información, consulte Crear una cuenta de almacenamiento.

También se puede crear una cuenta de Azure Storage mediante Azure PowerShell o la CLI de Azure.

Si prefiere no crear una cuenta de almacenamiento en este momento, también puede utilizar el emulador de Azure Storage para ejecutar y probar el código en un entorno local. Para más información, consulte Uso del emulador de Azure Storage para desarrollo y pruebas.

Creación de una cuenta de Azure Cosmos DB for Table

Para obtener instrucciones sobre cómo crear una cuenta de Azure Cosmos DB for Table, consulte Creación de una cuenta de base de datos.

Configuración de la aplicación para acceder a Table Storage

Para usar Azure Storage o Azure Cosmos DB necesitará el SDK de Azure Tables para Node.js, que incluye un conjunto de útiles bibliotecas que se comunican con los servicios REST de Storage.

Uso del Administrador de paquetes para Node (NPM) para instalar el paquete

  1. Use una interfaz de línea de comandos como PowerShell (Windows), Terminal (Mac) o Bash (Unix) y vaya a la carpeta donde creó la aplicación.
  2. Escriba lo siguiente en la ventana de comandos:
   npm install @azure/data-tables
  1. Puede ejecutar manualmente el comando ls para comprobar si se ha creado una carpeta node_modules. Dentro de dicha carpeta, encontrará el paquete @azure/data-tables, que contiene las bibliotecas necesarias para el acceso a las tablas.

Importación del paquete

Agregue el código siguiente a la parte superior del archivo server.js de la aplicación:

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

Conexión a Azure Table service

Puede conectarse a la cuenta de Azure Storage o Azure Cosmos DB for Table. Obtenga la clave compartida o la cadena de conexión en función del tipo de cuenta que esté usando.

Creación del cliente de servicio Table a partir de una clave compartida

El módulo de Azure lee las variables de entorno AZURE_ACCOUNT, AZURE_ACCESS_KEY y AZURE_TABLES_ENDPOINT para obtener la información necesaria para conectarse a su cuenta de Azure Storage o Azure Cosmos DB. Si no se configuran estas variables de entorno, debe especificar la información de la cuenta al llamar a TableServiceClient. Por ejemplo, el código siguiente crea un objeto TableServiceClient:

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

const tableService = new TableServiceClient(
  endpoint,
  credential
);

Creación del cliente del servicio Table a partir de una cadena de conexión

Para agregar una conexión de cuenta de Azure Cosmos DB o Storage, cree un objeto TableServiceClient y especifique el nombre de la cuenta, la clave principal y el punto de conexión. Puede copiar estos valores de Configuración>Cadena de conexión en Azure Portal para la cuenta de Azure Cosmos DB o Storage. Por ejemplo:

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

Creación de una tabla

La llamada a createTable crea una tabla nueva con el nombre especificado si todavía no existe. El ejemplo siguiente crea una tabla llamada "mytable", si es que no existe todavía:

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

Creación del cliente Table

Para interactuar con una tabla, debe crear un objeto TableClient con las mismas credenciales que usó para crear TableServiceClient. TableClient también requiere el nombre de la tabla de destino.

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

Adición de una entidad a una tabla

Para agregar una entidad, primero cree un objeto que defina las propiedades de la entidad. Todas las entidades tienen que contener valores de partitionKey y rowKey, que son identificadores únicos de la entidad.

  • partitionKey: Determina la partición en la que se almacena la entidad.
  • rowKey: Identifica de manera única la entidad dentro de la partición.

Tanto partitionKey como rowKey deben ser valores de cadena.

Este es un ejemplo de la definición de una entidad. dueDate se define como un tipo de Date. La especificación del tipo es opcional y los tipos se deducen si no se especifican.

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

Nota:

Hay también un campo Timestamp para cada registro; Azure lo establece cuando se inserta o actualiza una entidad.

Para agregar una entidad a la tabla, pase el objeto de la entidad al método createEntity.

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

Si la operación se completa correctamente, result contiene la ETag y la información sobre la operación.

Respuesta de ejemplo:

{ 
  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'
}

Actualización de una entidad

Los distintos modos para los métodos updateEntity y upsertEntity:

  • Combinar: Actualiza una entidad al actualizar las propiedades de la entidad sin reemplazar la entidad existente.
  • Reemplazar: Actualiza una entidad existente al reemplazar toda la entidad.

El ejemplo siguiente demuestra cómo actualizar una entidad usando upsertEntity:

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

Si la entidad que se va a actualizar no existe, se produce un error en la operación. Por lo tanto, si desea almacenar una entidad independientemente de si ya existe, use upsertEntity.

El result de operaciones de actualización correctas contiene la etiqueta Etag de la entidad actualizada.

Trabajo con grupos de entidades

A veces resulta útil enviar varias operaciones juntas en un lote a fin de garantizar el procesamiento atómico por parte del servidor. Para ello, cree una matriz de operaciones y pásala al método submitTransaction en TableClient.

El ejemplo siguiente demuestra cómo enviar dos entidades en un lote:

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

En las operaciones por lotes realizadas correctamente, result contiene información de cada operación del lote.

Recuperación de una entidad por clave

Para devolver una entidad específica basada en los valores de partitionKey y rowKey, use el método getEntity.

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

Después de completar esta operación, result contiene la entidad.

Consulta de un conjunto de entidades

En el siguiente ejemplo se crea una consulta que devuelve los cinco elementos principales con un valor de partitionKey de "hometasks", y se enumeran todas las entidades de la tabla.

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);
}

Consulta de un subconjunto de propiedades de las entidades

Una consulta de tabla puede recuperar solo algunos campos de una entidad. Esto reduce el ancho de banda y puede mejorar el rendimiento de las consultas, en especial en el caso de entidades de gran tamaño. Use la cláusula select y pase los nombres de los campos que se van a devolver. Por ejemplo, la siguiente consulta solo devuelve los campos description y 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;
}

Eliminación de una entidad

Puede eliminar una entidad usando sus claves de partición y fila. En este ejemplo, el objeto task1 contiene los valores de rowKey y partitionKey de la entidad que se va a eliminar. A continuación, el objeto pasa al método deleteEntity .

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

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

Nota:

Cuando elimine elementos, debería considerar el uso de etiquetas ETag para garantizar que otro proceso no haya modificado el elemento. Consulte Actualización de una entidad para obtener información sobre el uso de etiquetas ETag.

Eliminar una tabla

El código siguiente elimina una tabla de la cuenta de almacenamiento.

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

Usar tokens de continuación

Cuando consulte tablas para grandes cantidades de resultados, busque tokens de continuación. Puede haber grandes cantidades de datos disponibles para su consulta de los que podría no darse cuenta si no crear para reconocer cuando hay un token de continuación presente.

El objeto results que se devuelve al consultar los conjuntos de entidades, establece una propiedad continuationToken cuando hay un token de este tipo presente. Entonces, podrá usarlo al realizar una consulta para continuar moviéndose por las entidades de tabla y partición.

Al consultar, puede proporcionar un parámetro continuationToken entre la instancia de objeto de consulta y la función de devolución de llamada:

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);
   }
 }

Trabajo con firmas de acceso compartido

Las firmas de acceso compartido (SAS) constituyen una manera segura de ofrecer acceso granular a las tablas sin proporcionar el nombre o las claves de su cuenta de almacenamiento. Las SAS se usan con frecuencia para proporcionar acceso limitado a sus datos, por ejemplo, para permitir que una aplicación móvil consulte registros.

Una aplicación de confianza, como un servicio basado en la nube, genera una SAS mediante el valor generateTableSas del elemento TableClient, y se lo proporciona a una aplicación en la que no se confía o en la que se confía parcialmente, como una aplicación móvil. La SAS se genera usando una directiva que describe las fechas de inicio y de finalización durante las cuales la SAS es válida, junto con el nivel de acceso otorgado al titular de la SAS.

En el ejemplo siguiente se genera una nueva directiva de acceso compartido que permitirá al titular de SAS consultar ('r') la tabla.

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
});

La aplicación cliente usa entonces la SAS con AzureSASCredential para realizar operaciones en la tabla. En el siguiente ejemplo se realiza la conexión a la tabla y se realiza una consulta. Consulte en el artículo Otorgar acceso limitado a recursos de Azure Storage con firmas de acceso compartido (SAS) el formato de tableSAS.

// 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}` }
});

Como la SAS se generó solo con acceso de consulta, se devuelve un error si intenta insertar, actualizar o eliminar entidades.

Listas de control de acceso

Se puede usar una lista de control de acceso (ACL) para definir la directiva de acceso para una SAS. Esto es útil si desea permitir que varios clientes accedan a la tabla, pero cada uno con directivas de acceso diferentes.

Una ACL se implementa mediante el uso de un conjunto de directivas de acceso, con un Id. asociado a cada directiva. En los siguientes ejemplos se definen dos directivas; una para "user1" y otra para "user2":

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

En el siguiente ejemplo se obtiene la ACL actual para la tabla hometasks y luego se agregan las nuevas directivas mediante setAccessPolicy. Este enfoque permite lo siguiente:

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

Después de establecer una ACL, puede crear luego una SAS basada en el identificador de una directiva. En el siguiente ejemplo se crea una nueva SAS para 'user2':

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

Pasos siguientes

Para obtener más información, vea los recursos siguientes.