

# Node.js y DAX
<a name="DAX.client.run-application-nodejs-3"></a>

# Configuración predeterminada de cliente para Node.js
<a name="DAX-client-config-JS"></a>

Al configurar el cliente del SDK de JavaScript de DAX, puede personalizar varios parámetros para optimizar el rendimiento, la gestión de la conexión y la resiliencia a errores. En la siguiente tabla se describe la configuración predeterminada que controla la forma en que el cliente interactúa con el clúster de DAX, incluidos los valores de tiempo de espera, los mecanismos de reintento, la administración de credenciales y las opciones de supervisión del estado. Para obtener más información, consulte [Operaciones de DynamoDBClient](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/).


**Valores predeterminados del cliente del SDK de JS de DAX**  

| Parámetro | Tipo | Descripción | 
| --- | --- | --- | 
|  `region` opcional  |  `string`  |  La Región de AWS que se debe usar para el cliente de DAX (por ejemplo, “us-east-1”). Este parámetro es obligatorio si no se proporciona a través de la variable de entorno.  | 
|  `endpoint` obligatorio  |  `string`  | El punto de conexión del clúster al que se conecta el SDK. Ejemplos: No cifrado: dax-cluster-name.region.amazonaws.com Cifrado: daxs://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com  | 
|  `requestTimeout` predeterminado: 6000 ms  |  `number`  | Esto define el tiempo máximo que el cliente esperará una respuesta de DAX.  | 
|  `writeRetries` predeterminado: 1  |  `number`  | El número de reintentos que se realizarán para las solicitudes de escritura que fallan.  | 
|  `readRetries` predeterminado: 1  |  `number`  | El número de reintentos que se realizarán para las solicitudes de lectura que fallan.  | 
|  `maxRetries` predeterminado: 1  |  `number`  | El número máximo de reintentos para las solicitudes fallidas. Si se establecen readRetries/writeRetries, la configuración establecida en readRetries y writeRetries tiene prioridad sobre maxRetries.  | 
|  `connectTimeout` predeterminado: 10000 ms  |  `number`  | El tiempo de espera (en milisegundos) para establecer una conexión con cualquiera de los nodos del clúster.  | 
|  `maxConcurrentConnections` predeterminado: 100  |  `number`  | Limita el número total de conexiones simultáneas que una instancia de cliente puede crear por nodo en un clúster de DAX.  | 
|  `maxRetryDelay` predeterminado: 7000 ms  |  `number`  | Cuando el servidor DAX indica que es necesaria una recuperación estableciendo el indicador `waitForRecoveryBeforeRetrying` en verdadero, el cliente hará una pausa antes de reintentar los intentos. Durante estos periodos de recuperación, el parámetro `maxRetryDelay` determina el tiempo máximo de espera entre reintentos. Esta configuración específica de recuperación solo se aplica cuando el servidor DAX está en modo de recuperación. Para todos los demás escenarios, el comportamiento de reintentos sigue uno de dos patrones: o bien un retraso exponencial basado en el recuento de reintentos (regido por los parámetros `writeRetries`, `readRetries` o `maxRetries`), o bien un reintento inmediato dependiendo del tipo de excepción.  | 
|  `credentials` opcional  |  `[AwsCredentialIdentity](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-credential-providers/)` \$1 `[AwsCredentialIdentityProvider](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-credential-providers/)`  |  Las credenciales de AWS que se utilizarán para la autenticación de solicitudes. Esto puede proporcionarse como AwsCredentialIdentity o AwsCredentialIdentityProvider. Si no se proporciona, el AWS SDK utilizará automáticamente la cadena de proveedores de credenciales predeterminada. Ejemplo: `\$1 accessKeyId: 'AKIA...', secretAccessKey: '...', sessionToken: '...' \$1` \$1 @default Usa la cadena del proveedor de credenciales de AWS predeterminada.  | 
|  `healthCheckInterval` predeterminado: 5000 ms  |  `number`  | El intervalo (en milisegundos) entre las comprobaciones de estado del clúster. Con un intervalo más bajo se realizarán las comprobaciones con mayor frecuencia.  | 
|  `healthCheckTimeout` predeterminado: 1000 ms  |  `number`  | El tiempo de espera (en milisegundos) para que se complete la comprobación de estado.  | 
|  `skipHostnameVerification` predeterminado: false  |  `boolean`  |  Omitir la verificación del nombre de host de las conexiones TLS. Esto no tiene ningún impacto en los clústeres sin cifrar. De forma predeterminada, se realiza la verificación del nombre de host; si se establece en True, se omitirá la verificación. Asegúrese de comprender las implicaciones de desactivar esta opción, que es la imposibilidad de autenticar el clúster al que se está conectando.   | 
|  `unhealthyConsecutiveErrorCount` predeterminado: 5  |  `number`  | Establece el número de errores consecutivos necesarios para indicar que el nodo se encuentra en un estado incorrecto en el intervalo de comprobación de estado.  | 
|  `clusterUpdateInterval` predeterminado: 4000 ms  |  `number`  | Devuelve el intervalo entre sondeos de miembros del clúster para buscar cambios de pertenencia.  | 
|  `clusterUpdateThreshold` predeterminado: 125  |  `number`  | Devuelve el umbral por debajo del cual no se sondeará el clúster para buscar cambios de pertenencia.  | 
|  `credentailProvider` opcional \$1 predeterminado: nulo  |  `[AwsCredentialIdentityProvider](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-credential-providers/)`  | Proveedor definido por el usuario para las credenciales de AWS utilizadas para autenticar solicitudes a DAX.  | 


**Configuración de paginación para DaxDocument**  

| Nombre | Tipo | Detalle | 
| --- | --- | --- | 
|  `client`  |  DaxDocument  |  Instancia del tipo de DaxDocument.  | 
|  `pageSize`  |  número  |  Determina el número de elementos por página.  | 
|  `startingToken` Opcional  |  any  |  LastEvaluatedKey de la respuesta anterior se puede utilizar para solicitudes posteriores.  | 

Para obtener información sobre el uso de la paginación, consulte [TryDax.js](DAX.client.tutorial-TryDax.md).

# Migración al SDK de Node.js de DAX V3
<a name="DAX.client.run-application-nodejs-3-migrating"></a>

Esta guía de migración lo ayudará en la transición de las aplicaciones Node.js de DAX existentes. El nuevo SDK requiere Node.js 18 o superior e introduce varios cambios importantes en la forma en que estructurará el código de DynamoDB Accelerator. Esta guía lo guiará a través de las diferencias clave, incluidos los cambios de sintaxis, los nuevos métodos de importación y los patrones de programación asincrónica actualizados.

## Uso de DAX de Node.js V2
<a name="DAX.client.run-application-nodejs-3-migrating-V2-usage"></a>

```
const AmazonDaxClient = require('amazon-dax-client');
const AWS = require('aws-sdk');

var region = "us-west-2";

AWS.config.update({
  region: region,
});

var client = new AWS.DynamoDB.DocumentClient();

if (process.argv.length > 2) {
  var dax = new AmazonDaxClient({
    endpoints: [process.argv[2]],
    region: region,
  });
  client = new AWS.DynamoDB.DocumentClient({ service: dax });
}

// Make Get Call using Dax
var params = {
    TableName: 'TryDaxTable',
    pk: 1,
    sk: 1
}
client.get(params, function (err, data) {
    if (err) {
        console.error(
            "Unable to read item. Error JSON:",
            JSON.stringify(err, null, 2)
          );
    } else {
        console.log(data);
    }
});
```

## Uso de DAX de Node.js V3
<a name="DAX.client.run-application-nodejs-3-migrating-V3-dax-usage"></a>

Para utilizar Node.js V3 de DAX, la versión 18 o superior de Node es la versión preferida. Para cambiar a Node 18, utilice lo siguiente:

```
import { DaxDocument } from '@amazon-dax-sdk/lib-dax';
import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';

let client: DynamoDBDocument | DaxDocument = DynamoDBDocument.from(
  new DynamoDBClient({ region: 'us-west-2' })
);

if (process.argv.length > 2) {
  client = new DaxDocument({
    endpoints: [process.argv[2]],
    region: 'us-west-2',
  });
}

const params = {
  TableName: 'TryDaxTable',
  Key: { pk: 1, sk: 1 },
};

try {
  const results = await client.get(params);
  console.log(results);
} catch (err) {
  console.error(err);
}
```

El SDK de DAX para Node.js v3.x es compatible con el [AWS SDK para Node.js v3.x](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/). El SDK de DAX para Node.js v3.x admite el uso de clientes [agregados](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/#high-level-concepts). Tenga en cuenta que DAX no admite la creación de clientes básicos. Para obtener más información sobre las características no admitidas, consulte [Características que no están en paridad con AWS SDK V3](#DAX.client.run-application-nodejs-3-not-in-parity).

Siga estos pasos para ejecutar la aplicación de ejemplo de Node.js en su instancia de Amazon EC2.

**Para ejecutar el ejemplo de Node.js para DAX**

1. Configure Node.js en su instancia ,de Amazon EC2 de la siguiente manera:

   1. Instale el administrador de la versión de nodo (`nvm`).

      ```
      curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
      ```

   1. Use nvm para instalar Node.js.

      ```
      nvm install 18
      ```

   1. Uso de nvm para utilizar Node 18

      ```
      nvm use 18
      ```

   1. Compruebe que Node.js está instalado y se ejecuta correctamente.

      ```
      node -e "console.log('Running Node.js ' + process.version)"
      ```

      Esto debería mostrar el mensaje siguiente.

      `Running Node.js v18.x.x`

1. Instale el cliente Node.js de DaxDocument mediante el administrador del paquete de Node (`npm`).

   ```
   npm install @amazon-dax-sdk/lib-dax
   ```

## Código de muestra TryDax
<a name="DAX.client.run-application-nodejs-3-TryDax-sample-code"></a>

Para evaluar las ventajas de rendimiento de DynamoDB Accelerator (DAX), siga estos pasos para ejecutar una prueba de muestra que compare los tiempos de operación de lectura entre DynamoDB estándar y un clúster de DAX.

1. Después de configurar el espacio de trabajo y agregar `lib-dax` como dependencia, copie [TryDax.js](DAX.client.tutorial-TryDax.md) en el proyecto.

1. Ejecute el programa en el clúster de DAX. Para determinar el punto de enlace del clúster de DAX, elija una de las opciones siguientes: 
   +  **En la consola de DynamoDB**: elija su clúster de DAX. El punto de enlace del clúster se muestra en la consola, como en el siguiente ejemplo.

     ```
     dax://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com
     ```
   + **En la AWS CLI**: ingrese el siguiente comando.

     ```
     aws dax describe-clusters --query "Clusters[*].ClusterDiscoveryEndpoint"
     ```

     El punto de enlace del clúster se muestra en el resultado, como en el siguiente ejemplo.

     ```
     {
         "Address": "my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com",
         "Port": 8111,
         "URL": "dax://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com"
     }
     ```

1. Ahora ejecute el programa; para ello, especifique el punto de conexión del clúster como parámetro de línea de comandos.

   ```
   node TryDax.js dax://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com
   ```

   Debería ver una salida similar a esta:

   ```
   Attempting to create table; please wait...
   Successfully created table. Table status: ACTIVE
   Writing data to the table...
   Writing 20 items for partition key:  1
   Writing 20 items for partition key:  2
   Writing 20 items for partition key:  3
   ...
   Running GetItem Test
           Total time: 153555.10 µs - Avg time: 383.89 µs
           Total time: 44679.96 µs - Avg time: 111.70 µs
           Total time: 36885.86 µs - Avg time: 92.21 µs
           Total time: 32467.25 µs - Avg time: 81.17 µs
           Total time: 32202.60 µs - Avg time: 80.51 µs
   Running Query Test
           Total time: 14869.25 µs - Avg time: 2973.85 µs
           Total time: 3036.31 µs - Avg time: 607.26 µs
           Total time: 2468.92 µs - Avg time: 493.78 µs
           Total time: 2062.53 µs - Avg time: 412.51 µs
           Total time: 2178.22 µs - Avg time: 435.64 µs
   Running Scan Test
           Total time: 2395.88 µs - Avg time: 479.18 µs
           Total time: 2207.16 µs - Avg time: 441.43 µs
           Total time: 2443.14 µs - Avg time: 488.63 µs
           Total time: 2038.24 µs - Avg time: 407.65 µs
           Total time: 1972.17 µs - Avg time: 394.43 µs
   Running Pagination Test
   Scan Pagination
   [
     { pk: 1, sk: 1, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 2, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 3, someData: 'XXXXXXXXXX' }
   ]
   [
     { pk: 1, sk: 4, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 5, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 6, someData: 'XXXXXXXXXX' }
   ]
   ...
   Query Pagination
   [
     { pk: 1, sk: 1, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 2, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 3, someData: 'XXXXXXXXXX' }
   ]
   [
     { pk: 1, sk: 4, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 5, someData: 'XXXXXXXXXX' },
     { pk: 1, sk: 6, someData: 'XXXXXXXXXX' }
   ]
   ...
   Attempting to delete table; please wait...
   Successfully deleted table.
   ```

   Anote la información de tiempo. El número de microsegundos necesarios para las pruebas de `GetItem`, `Query` y `Scan`.

1. En este caso, ha ejecutado los programas en el clúster de DAX. Ahora, volverá a ejecutar el programa, esta vez en DynamoDB.

1. Ahora ejecute el programa de nuevo, pero esta vez, sin la URL del punto de conexión del clúster como parámetro de la línea de comandos.

   ```
   node TryDax.js
   ```

   Fíjese en el resultado y tome nota de la información sobre tiempos. Los tiempos transcurridos para las operaciones `GetItem`, `Query` y `Scan` deberían ser significativamente menores con DAX en comparación con DynamoDB.

## Características que no están en paridad con AWS SDK V3
<a name="DAX.client.run-application-nodejs-3-not-in-parity"></a>
+ [Clientes básicos](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/#high-level-concepts): Node.js de Dax V3 no admite clientes básicos. 

  ```
  const dynamoDBClient = new DynamoDBClient({ region: 'us-west-2' });
  const regularParams = {
      TableName: 'TryDaxTable',
      Key: {
          pk: 1,
          sk: 1
      }
  };
  // The DynamoDB client supports the send operation.
  const dynamoResult = await dynamoDBClient.send(new GetCommand(regularParams));
  
  // However, the DaxDocument client does not support the send operation.
  const daxClient = new DaxDocument({
      endpoints: ['your-dax-endpoint'],
      region: 'us-west-2',
  });
  
  const params = {
      TableName: 'TryDaxTable',
      Key: {
          pk: 1,
          sk: 1
      }
  };
  
  // This will throw an error - send operation is not supported for DAX. Please refer to documentation.
  const result = await daxClient.send(new GetCommand(params));
  console.log(result);
  ```
+ [Pila de middleware](https://aws.amazon.com/blogs/developer/middleware-stack-modular-aws-sdk-js/): Node.js de Dax V3 no admite el uso de funciones de middleware.

  ```
  const dynamoDBClient = new DynamoDBClient({ region: 'us-west-2' });
  // The DynamoDB client supports the middlewareStack.
  dynamoDBClient.middlewareStack.add(
    (next, context) =>> async (args) => {
      console.log("Before operation:", args);
      const result = await next(args);
      console.log("After operation:", result);
      return result;
    },
    {
      step: "initialize", // or "build", "finalizeRequest", "deserialize"
      name: "loggingMiddleware",
    }
  );
  
  // However, the DaxDocument client does not support the middlewareStack.
  const daxClient = new DaxDocument({
      endpoints: ['your-dax-endpoint'],
      region: 'us-west-2',
  });
  
  // This will throw an error - custom middleware and middlewareStacks are not supported for DAX. Please refer to documentation.
  daxClient.middlewareStack.add(
    (next, context) => async (args) => {
      console.log("Before operation:", args);
      const result = await next(args);
      console.log("After operation:", result);
      return result;
    },
    {
      step: "initialize", // or "build", "finalizeRequest", "deserialize"
      name: "loggingMiddleware",
    }
  );
  ```

# TryDax.js
<a name="DAX.client.tutorial-TryDax"></a>

```
import { DynamoDB, waitUntilTableExists, waitUntilTableNotExists } from "@aws-sdk/client-dynamodb";
import { DaxDocument, daxPaginateScan, daxPaginateQuery } from "@amazon-dax-sdk/lib-dax";
import { DynamoDBDocument, paginateQuery, paginateScan } from "@aws-sdk/lib-dynamodb";

const region = "us-east-1";
const tableName = "TryDaxTable";

// Determine the client (DynamoDB or DAX)
let client = DynamoDBDocument.from(new DynamoDB({ region }));
if (process.argv.length > 2) {
  client = new DaxDocument({ region, endpoint: process.argv[2] });
}

// Function to create table
async function createTable() {
  const dynamodb = new DynamoDB({ region });
  const params = {
    TableName: tableName,
    KeySchema: [
      { AttributeName: "pk", KeyType: "HASH" },
      { AttributeName: "sk", KeyType: "RANGE" },
    ],
    AttributeDefinitions: [
      { AttributeName: "pk", AttributeType: "N" },
      { AttributeName: "sk", AttributeType: "N" },
    ],
    ProvisionedThroughput: { ReadCapacityUnits: 25, WriteCapacityUnits: 25 },
  };

  try {
    console.log("Attempting to create table; please wait...");
    await dynamodb.createTable(params);
    await waitUntilTableExists({ client: dynamodb, maxWaitTime: 30 }, { TableName: tableName });
    console.log("Successfully created table. Table status: ACTIVE");
  } catch (err) {
    console.error("Error in table creation:", err);
  }
}

// Function to insert data
async function writeData() {
  console.log("Writing data to the table...");
  const someData = "X".repeat(10);
  for (let ipk = 1; ipk <= 20; ipk++) {
    console.log("Writing 20 items for partition key: ", ipk)
    for (let isk = 1; isk <= 20; isk++) {
      try {
        await client.put({ TableName: tableName, Item: { pk: ipk, sk: isk, someData } });
      } catch (err) {
        console.error("Error inserting data:", err);
      }
    }
  }
}

// Function to test GetItem
async function getItemTest() {
  console.log("Running GetItem Test");
  for (let i = 0; i < 5; i++) {
    const startTime = performance.now();
    const promises = [];
    for (let ipk = 1; ipk <= 20; ipk++) {
      for (let isk = 1; isk <= 20; isk++) {
        promises.push(client.get({ TableName: tableName, Key: { pk: ipk, sk: isk } }));
      }
    }
    await Promise.all(promises);
    const endTime = performance.now();
    const iterTime = (endTime - startTime) * 1000;
    const totalTime = iterTime.toFixed(2).padStart(3, ' ');
    const avgTime = (iterTime / 400).toFixed(2).padStart(3, ' ');
    console.log(`\tTotal time: ${totalTime} \u00B5s - Avg time: ${avgTime} \u00B5s`);
  }
}

// Function to test Query
async function queryTest() {
  console.log("Running Query Test");
  for (let i = 0; i < 5; i++) {
    const startTime = performance.now();
    const promises = [];
    for (let pk = 1; pk <= 5; pk++) {
      const params = {
        TableName: tableName,
        KeyConditionExpression: "pk = :pkval and sk between :skval1 and :skval2",
        ExpressionAttributeValues: { ":pkval": pk, ":skval1": 1, ":skval2": 2 },
      };
      promises.push(client.query(params));
    }
    await Promise.all(promises);
    const endTime = performance.now();
    const iterTime = (endTime - startTime) * 1000;
    const totalTime = iterTime.toFixed(2).padStart(3, ' ');
    const avgTime = (iterTime / 5).toFixed(2).padStart(3, ' ');
    console.log(`\tTotal time: ${totalTime} \u00B5s - Avg time: ${avgTime} \u00B5s`);
  }
}

// Function to test Scan
async function scanTest() {
  console.log("Running Scan Test");
  for (let i = 0; i < 5; i++) {
    const startTime = performance.now();
    const promises = [];
    for (let pk = 1; pk <= 5; pk++) {
      const params = {
        TableName: tableName,
        FilterExpression: "pk = :pkval and sk between :skval1 and :skval2",
        ExpressionAttributeValues: { ":pkval": pk, ":skval1": 1, ":skval2": 2 },
      };
      promises.push(client.scan(params));
    }
    await Promise.all(promises);
    const endTime = performance.now();
    const iterTime = (endTime - startTime) * 1000;
    const totalTime = iterTime.toFixed(2).padStart(3, ' ');
    const avgTime = (iterTime / 5).toFixed(2).padStart(3, ' ');
    console.log(`\tTotal time: ${totalTime} \u00B5s - Avg time: ${avgTime} \u00B5s`);
  }
}

// Function to test Pagination
async function paginationTest() {
  console.log("Running Pagination Test");
  console.log("Scan Pagination");
  const scanParams = { TableName: tableName };
  const paginator = process.argv.length > 2 ? daxPaginateScan : paginateScan;
  for await (const page of paginator({ client, pageSize: 3 }, scanParams)) {
    console.log(page.Items);
  }

  console.log("Query Pagination");
  const queryParams = {
    TableName: tableName,
    KeyConditionExpression: "pk = :pkval and sk between :skval1 and :skval2",
    ExpressionAttributeValues: { ":pkval": 1, ":skval1": 1, ":skval2": 10 },
  };
  const queryPaginator = process.argv.length > 2 ? daxPaginateQuery : paginateQuery;
  for await (const page of queryPaginator({ client, pageSize: 3 }, queryParams)) {
    console.log(page.Items);
  }
}

// Function to delete the table
async function deleteTable() {
  const dynamodb = new DynamoDB({ region });
  console.log("Attempting to delete table; please wait...")
  try {
    await dynamodb.deleteTable({ TableName: tableName });
    await waitUntilTableNotExists({ client: dynamodb, maxWaitTime: 30 }, { TableName: tableName });
    console.log("Successfully deleted table.");
  } catch (err) {
    console.error("Error deleting table:", err);
  }
}

// Execute functions sequentially
(async function () {
  await createTable();
  await writeData();
  await getItemTest();
  await queryTest();
  await scanTest();
  await paginationTest();
  await deleteTable();
})();
```