@npmtapi/tapi-lib-flow-orchestrator-dynamo-db
v1.0.3
Published
A library to manage DYnamoDB for flow orchestrator
Readme
Guía de Uso - tapi-lib-flow-orchestrator-dynamo-db
Descripción
tapi-lib-flow-orchestrator-dynamo-db es una librería de Node.js que proporciona una interfaz simplificada para interactuar con Amazon DynamoDB. Está diseñada específicamente para el orquestador de flujos de TAPI, ofreciendo métodos convenientes para operaciones CRUD y consultas avanzadas.
Instalación
npm install @npmtapi/tapi-lib-flow-orchestrator-dynamo-dbConfiguración
Variables de Entorno
La librería utiliza el paquete config para la configuración. Necesitas configurar las siguientes variables:
// config/default.js
module.exports = {
stateMachine: {
dynamoService: {
dynamoTableName: 'tu-tabla-dynamodb',
dynamoEndpoint: 'http://localhost:8000' // Solo para desarrollo local
}
},
env: 'development', // o 'production', 'test'
region: 'us-east-1' // o tu región de AWS
};Configuración de AWS
Asegúrate de tener configuradas tus credenciales de AWS:
export AWS_ACCESS_KEY_ID=tu-access-key
export AWS_SECRET_ACCESS_KEY=tu-secret-key
export AWS_REGION=us-east-1Uso Básico
Importar la librería
import { dynamoModel } from '@npmtapi/tapi-lib-flow-orchestrator-dynamo-db';
// Obtener instancia del modelo
const model = await dynamoModel();Operaciones CRUD
1. Crear un Item
// Crear un item simple
const newItem = await model.create({
id: 'user-123',
name: 'Juan Pérez',
email: '[email protected]',
createdAt: new Date().toISOString()
});
// Crear con opciones
const newItemWithOptions = await model.create({
id: 'user-124',
name: 'María García'
}, {
removeUndefinedValues: true // Elimina valores undefined
});2. Crear Item Solo Si No Existe
try {
const result = await model.createIfNotExists({
id: 'user-123',
name: 'Juan Pérez',
email: '[email protected]'
}, 'id'); // Verifica que 'id' no exista
console.log('Item creado exitosamente');
} catch (error) {
if (error.name === 'ConditionalCheckFailedException') {
console.log('El item ya existe');
}
}3. Buscar un Item
// Buscar por clave primaria simple
const item = await model.findOne({
key: { id: 'user-123' }
});
// Buscar por clave compuesta
const item = await model.findOne({
key: {
PK: 'user-123',
SK: 'profile'
}
});
if (item) {
console.log('Item encontrado:', item);
} else {
console.log('Item no encontrado');
}4. Actualizar un Item
// Actualizar atributos específicos
const updatedItem = await model.update(
{
name: 'Juan Carlos Pérez',
status: 'active',
updatedAt: new Date().toISOString()
},
{ id: 'user-123' } // Clave para identificar el item
);
console.log('Item actualizado:', updatedItem);5. Eliminar un Item
// Eliminar por clave primaria
const result = await model.delete({
Key: { id: 'user-123' }
});
// Eliminar por clave compuesta
const result = await model.delete({
Key: {
PK: 'user-123',
SK: 'profile'
}
});Consultas Avanzadas
1. Buscar Todos los Items (Scan)
// Buscar todos los items
const { items } = await model.findAll({});
// Buscar con filtro
const { items } = await model.findAll({
filterExpression: 'status = :status',
expressionAttributeValues: {
':status': 'active'
}
});
// Buscar con límite
const { items } = await model.findAll({
limit: 10
});
// Buscar todos ignorando límites por defecto
const { items } = await model.findAll({
skipIterationsLimit: true
});2. Consultas por Partition Key (Query)
// Consulta básica por partition key
const { items } = await model.queryAll({
keyConditionExpression: 'PK = :pk',
expressionAttributeValues: {
':pk': 'user-123'
}
});
// Consulta con filtro adicional
const { items } = await model.queryAll({
keyConditionExpression: 'PK = :pk',
expressionAttributeValues: {
':pk': 'user-123'
},
filterExpression: 'status = :status',
expressionAttributeValues: {
':pk': 'user-123',
':status': 'active'
}
});
// Consulta con ordenamiento descendente
const { items } = await model.queryAll({
keyConditionExpression: 'PK = :pk',
expressionAttributeValues: {
':pk': 'user-123'
},
scanIndexForward: false, // Orden descendente
limit: 5
});3. Consultas con Índices Secundarios
// Consulta usando Global Secondary Index (GSI)
const { items } = await model.queryAll({
keyConditionExpression: 'GSI1PK = :pk',
expressionAttributeValues: {
':pk': 'email-index'
},
indexKey: 'GSI1' // Nombre del índice
});
// Consulta usando Local Secondary Index (LSI)
const { items } = await model.queryAll({
keyConditionExpression: 'PK = :pk AND LSI1SK = :sk',
expressionAttributeValues: {
':pk': 'user-123',
':sk': 'profile'
},
indexKey: 'LSI1'
});4. Obtener el Primer y Último Item
// Obtener el item más reciente
const lastItem = await model.queryLast({
keyConditionExpression: 'PK = :pk',
expressionAttributeValues: {
':pk': 'chat-session-123'
}
});
// Obtener el item más antiguo
const firstItem = await model.queryFirst({
keyConditionExpression: 'PK = :pk',
expressionAttributeValues: {
':pk': 'chat-session-123'
}
});Operaciones en Lote
Crear Múltiples Items
const itemsToCreate = [
{ id: 'user-1', name: 'Juan', email: '[email protected]' },
{ id: 'user-2', name: 'María', email: '[email protected]' },
{ id: 'user-3', name: 'Carlos', email: '[email protected]' },
// ... más items
];
const results = await model.bulkCreate({
rows: itemsToCreate,
chunkSize: 25 // Máximo 25 items por lote (límite de DynamoDB)
});
// Verificar resultados
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Lote ${index + 1} procesado exitosamente`);
} else {
console.log(`Error en lote ${index + 1}:`, result.reason);
}
});Manejo de Errores
Errores Personalizados
import { BulkUnprocessedItems } from '@npmtapi/tapi-lib-flow-orchestrator-dynamo-db';
try {
const result = await model.create({
// ... datos
});
} catch (error) {
if (error.code === 'MissingRequiredParameter') {
console.log('Falta un parámetro requerido');
} else if (error instanceof BulkUnprocessedItems) {
console.log('Algunos items no pudieron ser procesados:', error.processedItemsCount);
} else {
console.log('Error de DynamoDB:', error.message);
}
}Errores Comunes
- MissingRequiredParameter: Se lanza cuando no se proporcionan parámetros requeridos
- ConditionalCheckFailedException: Se lanza cuando
createIfNotExistsfalla porque el item ya existe - BulkUnprocessedItems: Se lanza cuando algunos items en operaciones en lote no pudieron ser procesados
Ejemplos de Casos de Uso
1. Sistema de Usuarios
// Crear usuario
const user = await model.create({
PK: 'USER#123',
SK: 'PROFILE',
name: 'Juan Pérez',
email: '[email protected]',
status: 'active',
createdAt: new Date().toISOString()
});
// Buscar usuario
const user = await model.findOne({
key: {
PK: 'USER#123',
SK: 'PROFILE'
}
});
// Actualizar estado del usuario
const updatedUser = await model.update(
{ status: 'inactive' },
{ PK: 'USER#123', SK: 'PROFILE' }
);2. Sistema de Mensajes
// Crear mensaje
const message = await model.create({
PK: 'CHAT#123',
SK: `MSG#${Date.now()}`,
content: 'Hola, ¿cómo estás?',
sender: 'user-456',
timestamp: new Date().toISOString()
});
// Obtener últimos 10 mensajes del chat
const { items } = await model.queryAll({
keyConditionExpression: 'PK = :pk',
expressionAttributeValues: {
':pk': 'CHAT#123'
},
scanIndexForward: false, // Orden descendente
limit: 10
});
// Obtener el mensaje más reciente
const lastMessage = await model.queryLast({
keyConditionExpression: 'PK = :pk',
expressionAttributeValues: {
':pk': 'CHAT#123'
}
});3. Sistema de Notificaciones
// Crear notificación
const notification = await model.create({
PK: 'USER#123',
SK: `NOTIF#${Date.now()}`,
type: 'email',
subject: 'Bienvenido',
content: 'Gracias por registrarte',
read: false,
createdAt: new Date().toISOString()
});
// Buscar notificaciones no leídas
const { items } = await model.queryAll({
keyConditionExpression: 'PK = :pk',
expressionAttributeValues: {
':pk': 'USER#123'
},
filterExpression: 'read = :read',
expressionAttributeValues: {
':pk': 'USER#123',
':read': false
}
});Configuración para Desarrollo Local
Para desarrollo local, puedes usar DynamoDB Local:
// config/development.js
module.exports = {
stateMachine: {
dynamoService: {
dynamoTableName: 'local-table',
dynamoEndpoint: 'http://localhost:8000'
}
},
env: 'development',
region: 'us-east-1'
};Y ejecutar DynamoDB Local con Docker:
docker run -p 8000:8000 amazon/dynamodb-localMejores Prácticas
- Usar claves compuestas: Utiliza patrones como
PK#123ySK#PROFILEpara mejor organización - Índices apropiados: Crea GSI/LSI según tus patrones de consulta
- Manejo de errores: Siempre maneja las excepciones apropiadamente
- Operaciones en lote: Usa
bulkCreatepara insertar múltiples items - Filtros eficientes: Usa
queryAllen lugar defindAllcuando sea posible
Limitaciones
- Máximo 25 items por operación en lote (límite de DynamoDB)
- Las consultas
findAllpueden ser costosas para tablas grandes - Los índices secundarios tienen limitaciones de tamaño y rendimiento
Soporte
Para soporte técnico o reportar problemas, contacta al equipo de TAPI.
