Created
June 1, 2026 07:51
-
-
Save coderbyheart/0335d3595d10726c6d7dbaf991723809 to your computer and use it in GitHub Desktop.
wipe-tables.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Wipes all items from every DynamoDB table whose name starts with the given | |
| * prefix. | |
| * | |
| * Lists all tables in the account, keeps the ones matching the prefix, and for | |
| * each one scans every item (projecting only the key attributes) and deletes | |
| * them in batches of 25 via `BatchWriteItem`. Tables with deletion protection | |
| * enabled are skipped. The table itself is left in place — only its contents | |
| * are removed. | |
| * | |
| * Intended as an operator script for tearing down CI stack data. | |
| * | |
| * Usage: | |
| * node --experimental-transform-types --no-warnings wipe-tables.ts [prefix] | |
| */ | |
| import { | |
| BatchWriteItemCommand, | |
| DescribeTableCommand, | |
| DynamoDBClient, | |
| paginateListTables, | |
| paginateScan, | |
| } from '@aws-sdk/client-dynamodb' | |
| const db = new DynamoDBClient() | |
| const prefix = process.argv[2] | |
| if (prefix === undefined) { | |
| console.error('Usage: ts-node wipe-tables.ts [prefix]') | |
| process.exit(1) | |
| } | |
| // Get all tables prefixed with the provided prefix | |
| const tables: string[] = [] | |
| for await (const { TableNames } of paginateListTables({ client: db }, {})) { | |
| tables.push(...(TableNames ?? []).filter((name) => name.startsWith(prefix))) | |
| } | |
| for (const TableName of tables) { | |
| const { Table } = await db.send(new DescribeTableCommand({ TableName })) | |
| // Check that deletion protection is disabled | |
| if (Table?.DeletionProtectionEnabled === true) { | |
| console.log(`Skipping ${TableName}: deletion protection is enabled`) | |
| continue | |
| } | |
| const keyAttributes = (Table?.KeySchema ?? []).map( | |
| ({ AttributeName }) => AttributeName as string, | |
| ) | |
| // Delete all items in the table | |
| let deleted = 0 | |
| let batch: Record<string, unknown>[] = [] | |
| const flush = async () => { | |
| if (batch.length === 0) return | |
| await db.send( | |
| new BatchWriteItemCommand({ | |
| RequestItems: { | |
| [TableName]: batch.map((Key) => ({ | |
| DeleteRequest: { Key: Key as never }, | |
| })), | |
| }, | |
| }), | |
| ) | |
| deleted += batch.length | |
| batch = [] | |
| } | |
| for await (const { Items } of paginateScan( | |
| { client: db }, | |
| { | |
| TableName, | |
| ProjectionExpression: keyAttributes.map((_, i) => `#k${i}`).join(', '), | |
| ExpressionAttributeNames: Object.fromEntries( | |
| keyAttributes.map((name, i) => [`#k${i}`, name]), | |
| ), | |
| }, | |
| )) { | |
| for (const item of Items ?? []) { | |
| batch.push(item) | |
| // BatchWriteItem allows at most 25 requests per call | |
| if (batch.length === 25) await flush() | |
| } | |
| } | |
| await flush() | |
| console.log(`Wiped ${TableName}: deleted ${deleted} items`) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment