{"_id":"@daniadelapuenteveliz/dynamo-query-builder","_rev":"2-ecfd78ff184ad7659462db90dd4ed1d7","name":"@daniadelapuenteveliz/dynamo-query-builder","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@daniadelapuenteveliz/dynamo-query-builder","version":"1.0.0","keywords":["dynamodb","aws","query","builder","typescript","wrapper","database"],"author":{"name":"Dania Ignacia de la Puente Véliz"},"license":"MIT","_id":"@daniadelapuenteveliz/dynamo-query-builder@1.0.0","maintainers":[{"name":"daniadelapuenteveliz","email":"daniaignaciadelapuenteveliz@gmail.com"}],"homepage":"https://github.com/daniadelapuenteveliz/dynamo-query-builder-typescript#readme","bugs":{"url":"https://github.com/daniadelapuenteveliz/dynamo-query-builder-typescript/issues"},"dist":{"shasum":"bdfcf23481b184e4a5dff01828924d3dd236c2bb","tarball":"https://registry.npmjs.org/@daniadelapuenteveliz/dynamo-query-builder/-/dynamo-query-builder-1.0.0.tgz","fileCount":33,"integrity":"sha512-5lA69LLzSiEm5bSLDECO5qbhYpqvunxShbDOeTlHj9FzyB43OtYQxIJhs79kofzhrn8s/upbLEX5+g+8WS0nJw==","signatures":[{"sig":"MEQCIBj+m4ghI/i0nIffMWCYmHbTIFGMdLKts4T4ByE2TktuAiAq4TeVHn+vkfhB2smr66yeBDx8JkFGwtVx9Ysv4m4T1w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":178822},"main":"lib/index.js","types":"lib/index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"8bb80549240f754295cda1611681b271e2c9a46b","scripts":{"dev":"npm run build:watch","lint":"eslint src/**/*.ts","test":"jest","build":"tsc","clean":"rimraf lib","format":"prettier --write \"src/**/*.ts\"","lint:fix":"eslint src/**/*.ts --fix","test:watch":"jest --watch","build:watch":"tsc --watch","test:coverage":"jest --coverage","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"daniadelapuenteveliz","email":"daniaignaciadelapuenteveliz@gmail.com"},"repository":{"url":"git+https://github.com/daniadelapuenteveliz/dynamo-query-builder-typescript.git","type":"git"},"_npmVersion":"10.9.2","description":"A powerful TypeScript/JavaScript wrapper for creating and managing DynamoDB queries with a fluent API","directories":{},"_nodeVersion":"22.15.0","dependencies":{"@aws-sdk/util-dynamodb":"^3.450.0","@aws-sdk/client-dynamodb":"^3.450.0"},"publishConfig":{"access":"restricted"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.53.0","rimraf":"^5.0.5","ts-jest":"^29.1.1","prettier":"^3.6.2","typescript":"^5.2.2","@types/jest":"^29.5.8","@types/node":"^20.8.10","@typescript-eslint/parser":"^6.10.0","@typescript-eslint/eslint-plugin":"^6.10.0"},"_npmOperationalInternal":{"tmp":"tmp/dynamo-query-builder_1.0.0_1775947109823_0.005105391348011246","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@daniadelapuenteveliz/dynamo-query-builder","version":"1.0.1","description":"A powerful TypeScript/JavaScript wrapper for creating and managing DynamoDB queries with a fluent API","main":"lib/index.js","types":"lib/index.d.ts","scripts":{"build":"tsc","build:watch":"tsc --watch","clean":"rimraf lib","test":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage","lint":"eslint src/**/*.ts","lint:fix":"eslint src/**/*.ts --fix","format":"prettier --write \"src/**/*.ts\"","prepublishOnly":"npm run clean && npm run build","dev":"npm run build:watch"},"keywords":["dynamodb","aws","query","builder","typescript","wrapper","database"],"author":{"name":"Dania Ignacia de la Puente Véliz"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/daniadelapuenteveliz/dynamo-query-builder-typescript.git"},"bugs":{"url":"https://github.com/daniadelapuenteveliz/dynamo-query-builder-typescript/issues"},"homepage":"https://github.com/daniadelapuenteveliz/dynamo-query-builder-typescript#readme","engines":{"node":">=14.0.0"},"dependencies":{"@aws-sdk/client-dynamodb":"^3.450.0","@aws-sdk/util-dynamodb":"^3.450.0"},"devDependencies":{"@types/jest":"^29.5.8","@types/node":"^20.8.10","@typescript-eslint/eslint-plugin":"^6.10.0","@typescript-eslint/parser":"^6.10.0","eslint":"^8.53.0","jest":"^29.7.0","prettier":"^3.6.2","rimraf":"^5.0.5","ts-jest":"^29.1.1","typescript":"^5.2.2"},"publishConfig":{"access":"restricted"},"_id":"@daniadelapuenteveliz/dynamo-query-builder@1.0.1","gitHead":"abce1fe9b6457be697cd3592cf1619f6731122c8","_nodeVersion":"22.15.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-TsLtS52OXdAA6SghM4+z1NNsNdH7LxR3sHGjB/x+Mo9TfYjf4Polqwo+jnbTOrJj2tRz3ZatxkuPyzKfEDyjPQ==","shasum":"46e78f6c01decb67ed2cc67c7220a15440b3bffa","tarball":"https://registry.npmjs.org/@daniadelapuenteveliz/dynamo-query-builder/-/dynamo-query-builder-1.0.1.tgz","fileCount":33,"unpackedSize":179161,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCPOv8u7soy9PyRIjRjq1wZlJtGKfZztNqF+EPCDxEhBQIgH5+ab/3XKgacvNEqmu7u1bB49NeLFOaROC+WppKNP9c="}]},"_npmUser":{"name":"daniadelapuenteveliz","email":"daniaignaciadelapuenteveliz@gmail.com"},"directories":{},"maintainers":[{"name":"daniadelapuenteveliz","email":"daniaignaciadelapuenteveliz@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dynamo-query-builder_1.0.1_1777846594722_0.188569616510619"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-11T22:38:29.683Z","modified":"2026-05-03T22:16:34.999Z","1.0.0":"2026-04-11T22:38:30.000Z","1.0.1":"2026-05-03T22:16:34.875Z"},"bugs":{"url":"https://github.com/daniadelapuenteveliz/dynamo-query-builder-typescript/issues"},"author":{"name":"Dania Ignacia de la Puente Véliz"},"license":"MIT","homepage":"https://github.com/daniadelapuenteveliz/dynamo-query-builder-typescript#readme","keywords":["dynamodb","aws","query","builder","typescript","wrapper","database"],"repository":{"type":"git","url":"git+https://github.com/daniadelapuenteveliz/dynamo-query-builder-typescript.git"},"description":"A powerful TypeScript/JavaScript wrapper for creating and managing DynamoDB queries with a fluent API","maintainers":[{"name":"daniadelapuenteveliz","email":"daniaignaciadelapuenteveliz@gmail.com"}],"readme":"# dynamo-query-builder\n\nThis library is designed for managing objects stored in Amazon DynamoDB, leveraging partition key (PK) and sort key (SK) architecture for efficient data organization and querying.\n\n## Connecting to DynamoDB\n\nFirst, instantiate a `DynamoClient`. AWS credentials can be provided in two ways:\n\n### 1: Explicit IAM Credentials\nPass credentials directly in the configuration\n```typescript\nimport { DynamoClient, Config } from 'dynamo-query-builder';\n\nconst config: Config = {\n  region: 'YOUR_REGION',\n  credentials: {\n    accessKeyId: 'YOUR_ACCESS_KEY_ID',\n    secretAccessKey: 'YOUR_SECRET_ACCESS_KEY',\n  },\n};\n\nconst client = new DynamoClient(config);\n```\n\n### 2: Inherited Credentials\nUse the default AWS credential provider chain.\n\n```typescript\nconst config: Config = {};\nconst client = new DynamoClient(config);\n//or\nconst client2 = new DynamoClient();\n```\n\n## DynamoDB key in dynamo-query-builder\n\nDynamoDB uses two types of keys (must be strings in the current version of the library):\n\n- PK (Partition Key) — a hash key\n- SK (Sort Key) — a range key\n\nA common pattern in Dynamo design is to build these keys by concatenating multiple attributes that share a logical relationship or hierarchy.\n\nExample:\n\n```typescript\ntype message = {\n  sender_id: string;\n  channel: 'whatsapp' | 'mail' | 'sms';\n  receiver_id: string;\n  timestamp: string;\n  metadata: {\n    [key: string]: any;\n  };\n  attachment_urls: string[];\n};\n```\n\n- A message always belongs to a sender and a channel → good candidates for the PK.\n- The receiver gets the message at a particular timestamp → good candidates for the SK.\n\nThen, a consistent key structure could be:\n```typescript\nPK = sender_id#channel\nSK = receiver_id#timestamp\n```\n\nImportant technical notes: \n- Any key can be written as A#B#C....\n- It is recommended to separate only the SK, since PK queries are not efficient (you would rely on scans with filters, which should be avoided unless strictly necessary).\n- SK queries are always prefix-based. You cannot query only C; you must include A#B#C. This is why maintaining a clear hierarchy (A > B > C…) is important.\n\n\n\n## KeySchema\nA KeySchema defines how your PK and SK (optional) are constructed and represented in DynamoDB. Both PK and SK share the same structure:\n\n- name: Actual key name on DynamoDB\n- keys: Ordered string list representing the A#B#C... notation.\n- separator: string used to join the key components (# as default).\n\nExample:\n```typescript\nimport { KeySchema } from 'dynamo-query-builder';\n\nconst keySchema: KeySchema = {\n  pk: {\n    name: 'sender_channel',\n    keys: ['sender_id', 'channel'],\n    separator: '#',\n  },\n  sk: {\n    name: 'receiver_timestamp',\n    keys: ['receiver_id', 'timestamp'],\n    separator: '#',\n  },\n  preserve: ['sender_id', 'channel', 'receiver_id'], \n};\n```\nNote: preserve lets you store parts of a composite key as separate attributes.\nIf SK = A#B#C and you set preserve: ['B'], then B is also stored as its own DynamoDB attribute.\n\n## Typing\n\nDefining PK, SK, and data types ensures the library can store, infer, and validate your items correctly. This also helps maintain strong typing across your application.\n\nExample:\n```typescript\n// PK as DTO\ntype MessagePK = {\n  sender_id: string;\n  channel: 'whatsapp' | 'mail' | 'sms';\n};\n\n// SK as DTO\ntype MessageSK = {\n  receiver_id: string;\n  timestamp: string;\n};\n\n// Rest of the data\ntype MessageData = {\n  message_text: string;\n  metadata: {\n    [key: string]: any;\n  };\n  attachment_urls: string[];\n};\n\n// Complete Item DTO\ntype MessageDto = MessagePK & MessageSK & MessageData;\n```\n\n## Complete Example\n\n```typescript\nimport { DynamoClient, Config, KeySchema, Table } from 'dynamo-query-builder';\n\nconst config: Config = {\n  region: 'us-east-1',\n  credentials: {\n    accessKeyId: process.env.AWS_ACCESS_KEY_ID || '',\n    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '',\n  },\n};\n\nconst client = new DynamoClient(config);\n\ntype MessagePK = {\n  sender_id: string;\n  channel: 'whatsapp' | 'mail' | 'sms';\n};\n\ntype MessageSK = {\n  receiver_id: string;\n  timestamp: string;\n};\n\ntype MessageData = {\n  message_text: string;\n  metadata: {\n    [key: string]: any;\n  };\n  attachment_urls: string[];\n};\n\ntype MessageDto = MessagePK & MessageSK & MessageData;\n\nconst keySchema: KeySchema = {\n  pk: {\n    name: 'sender_channel',\n    keys: ['sender_id', 'channel'],\n    separator: '#',\n  },\n  sk: {\n    name: 'receiver_timestamp',\n    keys: ['receiver_id', 'timestamp'],\n    separator: '#',\n  },\n  preserve: ['sender_id', 'channel', 'receiver_id'],\n};\n\nconst messageTable: Table<MessagePK, MessageSK, MessageData> = \n  client.table<MessagePK, MessageSK, MessageData>('messages', keySchema);\n// Now the table is ready to be used. \n```\n\n## How to use it\n\n### Create Operations\n\n#### `put(item, override?)`\n\nInserts single item. Fails if item already exists unless `override=true`. Inserted item must contain PK and SK attributes.\n\n**Example:**\n```typescript\nawait messageTable.put({\n  sender_id: 'user123',\n  channel: 'whatsapp',\n  receiver_id: 'user456',\n  timestamp: '2024-01-01T00:00:00Z',\n  message_text: 'Hello!',\n  metadata: {},\n  attachment_urls: []\n});\n```\n#### `putBatch(items, override?)`\n\nPerforms an atomic batch insert of multiple items (up to 25). If any item fails, the entire transaction is rolled back. Inserted items must contain PK and SK attributes.\n\n**Example:**\n```typescript\nconst messages = [m1,m2];\nawait messageTable.putBatch(messages);\n```\n\n---\n\n### Update Operations\n\n#### `update(pk, sk, newData)`\n\nUpdates specific attributes of an existing item. Only the fields provided in `newData` will be updated; other attributes remain unchanged. The item must exist. Updated item must contain PK and SK attributes. Key attributes can't be updated.\n\n**Example:**\n```typescript\nawait messageTable.update(\n  { sender_id: 'user123', channel: 'whatsapp' },\n  { receiver_id: 'user456', timestamp: '2024-01-01T00:00:00Z' },\n  { message_text: 'Updated message text' }\n);\n```\n\n#### `updateBatch(updates)`\n\nPerforms an atomic batch update of multiple items (up to 25). Updated items must contain PK and SK attributes.\n\n**Example:**\n```typescript\nawait messageTable.updateBatch([\n  {\n    pk1,\n    sk1,\n    newData: { message_text: 'Updated 1' }\n  },\n  {\n    pk2,\n    sk2,\n    newData: { message_text: 'Updated 2' }\n  }\n]);\n```\n---\n\n### Delete Operations\n\n#### `delete(pk, sk)`\nDeletes a single item by its partition key and sort key.\n\n**Example:**\n```typescript\nawait messageTable.delete(\n  { sender_id: 'user123', channel: 'whatsapp' },\n  { receiver_id: 'user456', timestamp: '2024-01-01T00:00:00Z' }\n);\n```\n\n#### `deletePartition(pk)`\n\nDeletes all items that share the same partition key. This method queries the partition in batches of 25 and deletes items until the partition is empty.\n\n**Warning:** This operation may take a while for large partitions.\n\n**Example:**\n```typescript\nawait messageTable.deletePartition({ \n  sender_id: 'user123', \n  channel: 'whatsapp' \n});\n```\n\n**Limitations:**\n- Can be slow for partitions with many items.\n- No progress tracking or cancellation support.\n- Not atomic - partial deletions possible if operation is interrupted.\n\n#### `deleteBatch(deletes)`\n\nPerforms an atomic batch delete of multiple items (up to 25).\n\n**Example:**\n```typescript\nawait messageTable.deleteBatch([\n  {pk1,sk1},\n  {pk2,sk2}\n]);\n```\n---\n\n#### `deleteWithCondition(deleteParams)`\n\nDeletes items matching specific conditions. This method queries items by PK (and optional SK conditions), applies filters, and deletes matching items in batches until a limit is reached or all matching items are deleted.\n\n**Parameters:**\n- `deleteParams: SearchParams<PK, SK, DataDto>` - Search parameters:\n  - `pk: PK` - Partition key (required)\n  - `skCondition?: SKCondition<SK>` - Optional SK condition (equal, greaterThan, beginsWith, between, etc.)\n  - `filter?: FilterObject<DataDto>` - Optional filter on data attributes\n  - `limit?: number` - Maximum number of items to delete\n  - `IndexName?: string` - Optional GSI/LSI to query\n\n**Example:**\n```typescript\n// Delete all messages from a sender in a date range\nconst deleted = await messageTable.deleteWithCondition({\n  pk: { sender_id: 'user123', channel: 'whatsapp' },\n  skCondition: {\n    between: {\n      from: { receiver_id: 'user456', timestamp: '2024-01-01T00:00:00Z' },\n      to: { receiver_id: 'user456', timestamp: '2024-01-31T23:59:59Z' }\n    }\n  },\n  filter: { message_text: 'spam' }, // Only delete spam messages\n  limit: 100 // Delete at most 100 items\n});\n```\n\n**Limitations:**\n- Processes items in batches of 25 internally\n- Can be slow for large result sets\n- Not atomic across batches - partial deletions possible if interrupted\n\n\n#### `flush()`\n\nDeletes **all items** from the table. This method scans the entire table and deletes items in batches. \n\n**Warning:** This is a destructive operation that cannot be undone!\n\n**Example:**\n```typescript\nconst deletedCount = await messageTable.flush();\n```\n\n**Limitations:**\n- **Extremely slow** for large tables (scans entire table)\n- Not atomic - partial deletions possible if operation is interrupted\n- **No confirmation or safety checks** - use with extreme caution\n- Processes in batches of 25 internally\n\n---\n\n### Read Operations\n\n#### `getOne(pk, sk, IndexName?)`\n\nRetrieves a single item by PK and SK. Throws an error if the item is not found.\n\n**Example:**\n```typescript\nconst message = await messageTable.getOne(\n  { sender_id: 'user123', channel: 'whatsapp' },\n  { receiver_id: 'user456', timestamp: '2024-01-01T00:00:00Z' }\n);\n\n// Using a Global Secondary Index\nconst message = await messageTable.getOne(\n  { sender_id: 'user123', channel: 'whatsapp' },\n  { receiver_id: 'user456', timestamp: '2024-01-01T00:00:00Z' },\n  'GSI1'\n);\n```\n\n#### `getPartitionBatch(qparams)`\n\nRetrieves all items in a partition (items sharing the same PK). Supports pagination, sorting, and projection.\n\n**Parameters:**\n- `qparams: QueryParams<PK, DataDto>` - Query parameters:\n  - `pk: PK` - Partition key (required)\n  - `limit: number` - Maximum number of items to return\n  - `pagination?: Pagination` - Pagination options:\n    - `pivot?: KeyRec` - Last evaluated key from previous query\n    - `direction?: 'forward' | 'backward'` - Sort direction\n  - `project?: (keyof DataDto)[]` - Array of attribute names to return\n  - `IndexName?: string` - Optional GSI/LSI name\n\n**Returns:** `Promise<paginationResult>` - Object containing:\n  - `items: ItemOf<PK, SK, DataDto>[]` - Array of items\n  - `lastEvaluatedKey?: KeyRec` - Key for pagination\n  - `hasNext: boolean` - Whether more items exist\n  - `count: number` - Number of items returned\n\n**Example:**\n```typescript\n// Get first 50 items in a partition\nconst result = await messageTable.getPartitionBatch({\n  pk: { sender_id: 'user123', channel: 'whatsapp' },\n  limit: 50\n});\n\n// Paginate through results\nlet lastKey = result.lastEvaluatedKey;\nwhile (result.hasNext) {\n  const nextResult = await messageTable.getPartitionBatch({\n    pk: { sender_id: 'user123', channel: 'whatsapp' },\n    limit: 50,\n    pagination: { pivot: lastKey, direction: 'forward' }\n  });\n  lastKey = nextResult.lastEvaluatedKey;\n}\n\n// Project only specific attributes\nconst result = await messageTable.getPartitionBatch({\n  pk: { sender_id: 'user123', channel: 'whatsapp' },\n  limit: 50,\n  project: ['message_text', 'timestamp'] // Only return these fields\n});\n```\n\n**Limitations:**\n- Default limit is 50 if not specified\n- Returns items sorted ascending by default\n- Maximum 1MB of data per query (DynamoDB limit)\n- Projection reduces returned data but doesn't reduce read capacity units consumed\n\n\n#### `search(searchParams)`\n\nSearches for items matching specific conditions. This method queries by PK (and optional SK conditions), applies filters, and automatically paginates through all matching results up to an optional limit.\n\n**Parameters:**\n- `searchParams: SearchParams<PK, SK, DataDto>` - Search parameters:\n  - `pk: PK` - Partition key (required)\n  - `skCondition?: SKCondition<SK>` - Optional SK condition (equal, greaterThan, beginsWith, between, etc.)\n  - `filter?: FilterObject<DataDto>` - Optional filter on data attributes\n  - `limit?: number` - Maximum number of items to return\n  - `project?: (keyof DataDto)[]` - Array of attribute names to return\n  - `IndexName?: string` - Optional GSI/LSI name\n  - `pagination?: Pagination` - Pagination options\n\n**Returns:** `Promise<ItemOf<PK, SK, DataDto>[]>` - Array of all matching items (up to limit)\n\n**Example:**\n```typescript\n// Search for messages in a date range\nconst messages = await messageTable.search({\n  pk: { sender_id: 'user123', channel: 'whatsapp' },\n  skCondition: {\n    beginsWith: { receiver_id: 'user456' }\n  },\n  filter: { message_text: 'urgent' },\n  limit: 100\n});\n\n// Search with SK between condition\nconst messages = await messageTable.search({\n  pk: { sender_id: 'user123', channel: 'whatsapp' },\n  skCondition: {\n    between: {\n      from: { receiver_id: 'user456', timestamp: '2024-01-01T00:00:00Z' },\n      to: { receiver_id: 'user456', timestamp: '2024-01-31T23:59:59Z' }\n    }\n  }\n});\n```\n\n**Limitations:**\n- Processes items in batches of 25 internally\n- Can be slow for large result sets\n- Filter expressions are applied after query (less efficient than key conditions)\n- If limit is not provided, returns all matching items (may be slow/expensive)\n- Maximum 1MB of data per query batch (DynamoDB limit)\n\n### Query\n\nCreates a Query builder instance for constructing complex queries. Returns a `Query` object that supports method chaining for filtering, sorting, and pagination.\n\n**Parameters:**\n- `qparams: QueryParams<PK, DataDto>` - Query parameters:\n  - `pk: PK` - Partition key (required)\n  - `limit: number` - Maximum number of items per query\n  - `project?: (keyof DataDto)[]` - Array of attribute names to return\n  - `IndexName?: string` - Optional GSI/LSI name\n\n**Returns:** `Query<PK, SK, DataDto>` - Query builder instance\n\n**Example:**\n```typescript\n// Basic query\nconst query = messageTable.query({\n  pk: { sender_id: 'user123', channel: 'whatsapp' },\n  limit: 50\n});\n\n// Chain query methods\nconst result = await messageTable\n  .query({ pk: { sender_id: 'user123', channel: 'whatsapp' }, limit: 50 })\n  .whereSKequal({ receiver_id: 'user456', timestamp: '2024-01-01T00:00:00Z' })\n  .filter({ message_text: 'hello' })\n  .sortAscending()\n  .run();\n\n// Query with SK begins with\nconst result = await messageTable\n  .query({ pk: { sender_id: 'user123', channel: 'whatsapp' }, limit: 50 })\n  .whereSKBeginsWith({ receiver_id: 'user456' })\n  .run();\n```\n\n### Scan\n\nCreates a Scan builder instance for scanning the entire table. Returns a `Scan` object that supports method chaining for filtering and pagination.\n\n**Parameters:**\n- `sparams: ScanParams<DataDto>` - Scan parameters:\n  - `limit: number` - Maximum number of items per scan\n  - `project?: (keyof DataDto)[]` - Array of attribute names to return\n  - `IndexName?: string` - Optional GSI/LSI name\n\n**Example:**\n```typescript\n// Basic scan\nconst scan = messageTable.scan({ limit: 100 });\n\n// Chain scan methods\nconst result = await messageTable\n  .scan({ limit: 100 })\n  .filter({ message_text: 'spam' })\n  .run();\n\n// Scan with pagination\nlet lastKey;\ndo {\n  const result = await messageTable\n    .scan({ limit: 100 })\n    .pivot(lastKey)\n    .run();\n  lastKey = result.lastEvaluatedKey;\n} while (result.hasNext);\n```\n\n**Limitations:**\n- **Expensive operation** - scans entire table (or index)\n- **Slow** for large tables\n- Returns a Scan builder, not results (must call `.run()` to execute)\n- Filter expressions are applied after scan (consumes full read capacity)\n- Maximum 1MB of data per scan (DynamoDB limit)\n- **Avoid scans when possible** - use queries with PK instead\n\n### Raw methods\n\nRaw methods provide direct access to AWS SDK command inputs, bypassing the library's automatic formatting, validation, and type safety features. \n- `putRaw()` - Accepts `PutItemCommandInput` from `@aws-sdk/client-dynamodb`\n- `updateRaw()` - Accepts `UpdateItemCommandInput` from `@aws-sdk/client-dynamodb`\n- `deleteRaw()` - Accepts `DeleteItemCommandInput` from `@aws-sdk/client-dynamodb`\n- `queryRaw()` - Accepts `CommandInput` (custom type for Query operations)\n- `scanRaw()` - Accepts `CommandInput` (custom type for Scan operations)\n\n---\n### Metadata and Utility Operations\n\n- `getTableName()` - Returns the table name used by this table instance.\n- `getClient()` - Returns the underlying AWS DynamoDB client instance.\n- `getTableNameInDynamo()`- Retrieves the actual table name from DynamoDB by calling `DescribeTable`.\n- `getItemCount()` - Retrieves the approximate item count for the table from DynamoDB metadata.\n- `getDynamoKeySchema()` - Retrieves the DynamoDB key schema (PK and SK definitions) from the table metadata.\n- `getAttributeDefinitions()` - Retrieves the attribute definitions from the table metadata.\n- `getGlobalSecondaryIndexes()` - Retrieves the Global Secondary Indexes (GSI) definitions from the table metadata.\n- `describe()` - Retrieves the complete table description from DynamoDB (includes all metadata: schema, indexes, throughput, etc.).\n- `getKeySchema()` - Returns the KeySchema configuration used by this table instance (the schema provided during table creation).\n\n## What You CANNOT Do (for now...)\n- Change Metadata: Indexes (GSI, LSI), Payment method and Throughput settings.\n- Cannot Delete Tables\n- Non string Keys: Numeric or binary for the PK and SK\n\n## Future Improvements\n- Download table to CSV\n- Bulk import from CSV\n- locking support (mutex)\n- Enhanced connection options","readmeFilename":"README.md"}