{"id":"49e43b9e-3fc3-4e16-ad25-740485a29d87","shortId":"82phHU","kind":"skill","title":"typescript-dynamodb-toolbox","tagline":"DynamoDB single-table design using dynamodb-toolbox v2. Use when creating entities, defining key patterns, designing GSIs, writing queries, implementing pagination, or working with any DynamoDB data layer in TypeScript projects.","description":"# DynamoDB with dynamodb-toolbox v2\n\nType-safe DynamoDB interactions with Entity and Table abstractions for single-table design.\n\n## When to Use DynamoDB\n\n✓ **Use when:**\n- Access patterns are known upfront and stable\n- Need predictable sub-10ms performance at scale\n- Microservice with clear data boundaries\n- Willing to commit to single-table design\n\n✗ **Avoid when:**\n- Prototyping with fluid requirements\n- Need ad-hoc analytical queries\n- Team lacks DynamoDB expertise\n- GraphQL resolvers drive access patterns\n\n> DynamoDB inverts the relational paradigm: design for known access patterns, not flexible querying.\n\n## Modeling Checklist\n\nBefore implementing:\n1. **Define Entity Relationships** - Create ERD with all entities and relationships\n2. **Create Entity Chart** - Map each entity to PK/SK patterns\n3. **Design GSI Strategy** - Plan secondary access patterns\n4. **Document Access Patterns** - List every query the application needs\n\nSee [references/modeling.md](references/modeling.md) for detailed methodology.\n\n## Table Configuration\n\n```typescript\nimport { Table } from 'dynamodb-toolbox/table'\n\nconst AppTable = new Table({\n  name: process.env.TABLE_NAME || \"AppTable\",\n  partitionKey: { name: \"PK\", type: \"string\" },\n  sortKey: { name: \"SK\", type: \"string\" },\n  indexes: {\n    GSI1: {\n      type: \"global\",\n      partitionKey: { name: \"GSI1PK\", type: \"string\" },\n      sortKey: { name: \"GSI1SK\", type: \"string\" },\n    },\n    GSI2: {\n      type: \"global\",\n      partitionKey: { name: \"GSI2PK\", type: \"string\" },\n      sortKey: { name: \"GSI2SK\", type: \"string\" },\n    },\n    GSI3: {\n      type: \"global\",\n      partitionKey: { name: \"GSI3PK\", type: \"string\" },\n      sortKey: { name: \"GSI3SK\", type: \"string\" },\n    },\n  },\n  entityAttributeSavedAs: \"_et\", // default, customize if needed\n});\n```\n\n### Index Purpose\n\n| Index | Purpose |\n|-------|---------|\n| Main Table (PK/SK) | Primary entity access |\n| GSI1 | Collection queries (issues by repo, members by org) |\n| GSI2 | Entity-specific queries and relationships (forks) |\n| GSI3 | Hierarchical queries with temporal sorting (repos by owner) |\n\n## Entity Definition (v2 syntax)\n\n### Basic Pattern with Linked Keys\n\n```typescript\nimport { Entity } from 'dynamodb-toolbox/entity'\nimport { item } from 'dynamodb-toolbox/schema/item'\nimport { string } from 'dynamodb-toolbox/schema/string'\n\nconst UserEntity = new Entity({\n  name: \"USER\",\n  table: AppTable,\n  schema: item({\n    // Business attributes\n    username: string().required().key(),\n    email: string().required(),\n    bio: string().optional(),\n  }).and(_schema => ({\n    // Computed keys (PK/SK/GSI keys derived from business attributes)\n    PK: string().key().link<typeof _schema>(\n      ({ username }) => `ACCOUNT#${username}`\n    ),\n    SK: string().key().link<typeof _schema>(\n      ({ username }) => `ACCOUNT#${username}`\n    ),\n    GSI1PK: string().link<typeof _schema>(\n      ({ username }) => `ACCOUNT#${username}`\n    ),\n    GSI1SK: string().link<typeof _schema>(\n      ({ username }) => `ACCOUNT#${username}`\n    ),\n  })),\n});\n```\n\n### With Validation\n\n```typescript\nconst RepoEntity = new Entity({\n  name: \"REPO\",\n  table: AppTable,\n  schema: item({\n    owner: string()\n      .required()\n      .validate((value: string) => /^[a-zA-Z0-9_-]+$/.test(value))\n      .key(),\n    repo_name: string()\n      .required()\n      .validate((value: string) => /^[a-zA-Z0-9_-]+$/.test(value))\n      .key(),\n    description: string().optional(),\n    is_private: boolean().default(false),\n  }).and(_schema => ({\n    PK: string().key().link<typeof _schema>(\n      ({ owner, repo_name }) => `REPO#${owner}#${repo_name}`\n    ),\n    SK: string().key().link<typeof _schema>(\n      ({ owner, repo_name }) => `REPO#${owner}#${repo_name}`\n    ),\n    // GSI3 for temporal sorting (repos by owner, newest first)\n    GSI3PK: string().link<typeof _schema>(\n      ({ owner }) => `ACCOUNT#${owner}`\n    ),\n    GSI3SK: string()\n      .default(() => `#${new Date().toISOString()}`)\n      .savedAs(\"GSI3SK\"),\n  })),\n});\n```\n\n## Entity Chart (Key Patterns)\n\n| Entity | PK | SK | Purpose |\n|--------|----|----|---------|\n| User | `ACCOUNT#{username}` | `ACCOUNT#{username}` | Direct access |\n| Repository | `REPO#{owner}#{name}` | `REPO#{owner}#{name}` | Direct access |\n| Issue | `ISSUE#{owner}#{repo}#{padded_num}` | Same as PK | Direct access + enumeration |\n| Comment | `REPO#{owner}#{repo}` | `ISSUE#{padded_num}#COMMENT#{id}` | Comments under issue |\n| Star | `ACCOUNT#{username}` | `STAR#{owner}#{repo}#{timestamp}` | Adjacency list pattern |\n\n**Key Pattern Rules:**\n- `ENTITY#{id}` - Simple identifier\n- `PARENT#{id}#CHILD#{id}` - Hierarchy\n- `TYPE#{category}#{identifier}` - Categorization\n- `#{timestamp}` - Temporal sorting (# prefix ensures ordering)\n\n## Type Safety\n\n```typescript\nimport { type InputItem, type FormattedItem } from 'dynamodb-toolbox/entity'\n\n// Type exports\ntype UserRecord = typeof UserEntity\ntype UserInput = InputItem<typeof UserEntity>      // For writes\ntype UserFormatted = FormattedItem<typeof UserEntity> // For reads\n\n// Usage in entities\nclass User {\n  static fromRecord(record: UserFormatted): User { /* ... */ }\n  toRecord(): UserInput { /* ... */ }\n}\n```\n\nSee [references/entity-layer.md](references/entity-layer.md) for transformation patterns.\n\n## Repository Pattern\n\n```typescript\nimport { PutItemCommand, GetItemCommand, DeleteItemCommand } from 'dynamodb-toolbox'\n\nclass UserRepository {\n  constructor(private entity: UserRecord) {}\n\n  // CREATE with duplicate check\n  async create(user: User): Promise<User> {\n    try {\n      const result = await this.entity\n        .build(PutItemCommand)\n        .item(user.toRecord())\n        .options({\n          condition: { attr: \"PK\", exists: false }, // Prevent duplicates\n        })\n        .send()\n\n      return User.fromRecord(result.ToolboxItem)\n    } catch (error) {\n      if (error instanceof ConditionalCheckFailedException) {\n        throw new DuplicateEntityError(\"User\", user.username)\n      }\n      throw error\n    }\n  }\n\n  // GET by key\n  async get(username: string): Promise<User | undefined> {\n    const result = await this.entity\n      .build(GetItemCommand)\n      .key({ username })\n      .send()\n\n    return result.Item ? User.fromRecord(result.Item) : undefined\n  }\n\n  // UPDATE with existence check\n  async update(user: User): Promise<User> {\n    try {\n      const result = await this.entity\n        .build(PutItemCommand)\n        .item(user.toRecord())\n        .options({\n          condition: { attr: \"PK\", exists: true }, // Must exist\n        })\n        .send()\n\n      return User.fromRecord(result.ToolboxItem)\n    } catch (error) {\n      if (error instanceof ConditionalCheckFailedException) {\n        throw new EntityNotFoundError(\"User\", user.username)\n      }\n      throw error\n    }\n  }\n\n  // DELETE\n  async delete(username: string): Promise<void> {\n    await this.entity.build(DeleteItemCommand).key({ username }).send()\n  }\n}\n```\n\nSee [references/error-handling.md](references/error-handling.md) for error patterns.\n\n## Query Patterns\n\n### Query GSI\n\n```typescript\nimport { QueryCommand } from 'dynamodb-toolbox/table/actions/query'\n\n// List issues for a repository using GSI1\nasync listIssues(owner: string, repoName: string): Promise<Issue[]> {\n  const result = await this.table\n    .build(QueryCommand)\n    .entities(this.issueEntity)\n    .query({\n      partition: `ISSUE#${owner}#${repoName}`,\n      index: \"GSI1\",\n    })\n    .send()\n\n  return result.Items?.map(item => Issue.fromRecord(item)) || []\n}\n```\n\n### Query with Range Filter\n\n```typescript\n// List by status using beginsWith on SK\nasync listOpenIssues(owner: string, repoName: string): Promise<Issue[]> {\n  const result = await this.table\n    .build(QueryCommand)\n    .entities(this.issueEntity)\n    .query({\n      partition: `ISSUE#${owner}#${repoName}`,\n      index: \"GSI4\",\n      range: {\n        beginsWith: \"ISSUE#OPEN#\", // Filter to open issues only\n      },\n    })\n    .send()\n\n  return result.Items?.map(item => Issue.fromRecord(item)) || []\n}\n```\n\n### Pagination\n\n```typescript\n// Encode/decode pagination tokens\nfunction encodePageToken(lastEvaluated?: Record<string, unknown>): string | undefined {\n  return lastEvaluated\n    ? Buffer.from(JSON.stringify(lastEvaluated)).toString(\"base64\")\n    : undefined\n}\n\nfunction decodePageToken(token?: string): Record<string, unknown> | undefined {\n  return token ? JSON.parse(Buffer.from(token, \"base64\").toString()) : undefined\n}\n\n// Query with pagination\nasync listReposByOwner(owner: string, limit = 50, offset?: string) {\n  const result = await this.table\n    .build(QueryCommand)\n    .entities(this.repoEntity)\n    .query({\n      partition: `ACCOUNT#${owner}`,\n      index: \"GSI3\",\n      range: { lt: \"ACCOUNT#\" }, // Filter to only repos (not account itself)\n    })\n    .options({\n      reverse: true,                              // Newest first\n      exclusiveStartKey: decodePageToken(offset), // Continue from cursor\n      limit,\n    })\n    .send()\n\n  return {\n    items: result.Items?.map(item => Repo.fromRecord(item)) || [],\n    nextOffset: encodePageToken(result.LastEvaluatedKey),\n  }\n}\n```\n\n## Transactions\n\nSee [references/transactions.md](references/transactions.md) for:\n- Multi-entity transactions (PutTransaction + ConditionCheck)\n- Atomic counters with `$add(1)`\n- TransactionCanceledException handling\n\n## Testing\n\nSee [references/testing.md](references/testing.md) for:\n- DynamoDB Local setup\n- Test fixtures and factories\n- Concurrency and temporal sorting tests\n\n## Quick Reference\n\n**Schema:**\n- Use `item({})` for schema definition\n- Mark key attributes with `.key()`\n- Separate business attributes from computed keys using `.and()`\n- Use `.link<typeof _schema>()` to compute PK/SK/GSI keys\n- Use `.validate()` for field validation\n- Use `.savedAs()` when DynamoDB name differs from schema name\n\n**Types:**\n- `InputItem<T>` for writes (excludes computed attributes)\n- `FormattedItem<T>` for reads (includes all attributes)\n\n**Repository:**\n- Use `PutItemCommand` with `{ attr: \"PK\", exists: false }` for creates\n- Use `PutItemCommand` with `{ attr: \"PK\", exists: true }` for updates\n- Use `GetItemCommand` with `.key()` for reads\n- Use `QueryCommand` with `.entities()` for type-safe queries\n\n**Errors:**\n- `ConditionalCheckFailedException` → DuplicateEntityError (create) or EntityNotFoundError (update)\n- Always catch and convert to domain errors\n\n**Testing:**\n- Use unique IDs per test run (timestamp-based)\n- Clean up test data after each test\n- Use DynamoDB Local for development","tags":["typescript","dynamodb","toolbox","atelier","martinffx","agent-skills","agentic-coding","anthropic","claude-code","claude-skills","code-review","codex"],"capabilities":["skill","source-martinffx","skill-typescript-dynamodb-toolbox","topic-agent-skills","topic-agentic-coding","topic-anthropic","topic-claude-code","topic-claude-skills","topic-code-review","topic-codex","topic-codex-skill","topic-opencode","topic-prompt-engineering","topic-sdd","topic-spec-driven-development"],"categories":["atelier"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/martinffx/atelier/typescript-dynamodb-toolbox","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add martinffx/atelier","source_repo":"https://github.com/martinffx/atelier","install_from":"skills.sh"}},"qualityScore":"0.461","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 23 github stars · SKILL.md body (10,550 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:05:25.023Z","embedding":null,"createdAt":"2026-05-10T07:03:13.517Z","updatedAt":"2026-05-18T19:05:25.023Z","lastSeenAt":"2026-05-18T19:05:25.023Z","tsv":"'/.test':399,414 '/entity':302,564 '/schema/item':309 '/schema/string':316 '/table':185 '/table/actions/query':755 '1':131,954 '10ms':76 '2':142 '3':152 '4':160 '50':889 '9':398,413 'a-za-z0':394,409 'abstract':53 'access':65,112,122,158,162,259,486,495,506 'account':354,361,367,373,462,481,483,521,902,908,914 'ad':101 'ad-hoc':100 'add':953 'adjac':527 'alway':1069 'analyt':103 'applic':168 'apptabl':187,193,324,385 'async':620,662,687,727,763,805,884 'atom':950 'attr':636,703,1032,1041 'attribut':328,348,984,989,1021,1027 'avoid':93 'await':628,671,695,732,773,815,894 'base':1085 'base64':863,878 'basic':290 'beginswith':802,829 'bio':336 'boolean':422 'boundari':84 'buffer.from':859,876 'build':630,673,697,775,817,896 'busi':327,347,988 'catch':646,713,1070 'categor':545 'categori':543 'chart':145,473 'check':619,686 'checklist':128 'child':539 'class':584,610 'clean':1086 'clear':82 'collect':261 'comment':508,515,517 'commit':87 'comput':341,991,998,1020 'concurr':969 'condit':635,702 'conditionalcheckfailedexcept':651,718,1063 'conditioncheck':949 'configur':177 'const':186,317,378,626,669,693,771,813,892 'constructor':612 'continu':924 'convert':1072 'counter':951 'creat':17,135,143,616,621,1037,1065 'cursor':926 'custom':247 'data':33,83,1089 'date':468 'decodepagetoken':866,922 'default':246,423,466 'defin':19,132 'definit':287,981 'delet':726,728 'deleteitemcommand':605,734 'deriv':345 'descript':417 'design':9,22,58,92,119,153 'detail':174 'develop':1097 'differ':1011 'direct':485,494,505 'document':161 'domain':1074 'drive':111 'duplic':618,641 'duplicateentityerror':654,1064 'dynamodb':3,5,12,32,38,41,47,62,107,114,183,300,307,314,562,608,753,962,1009,1094 'dynamodb-toolbox':11,40,182,299,306,313,561,607,752 'email':333 'encode/decode':846 'encodepagetoken':850,937 'ensur':550 'entiti':18,50,133,139,144,148,258,271,286,297,320,381,472,476,533,583,614,777,819,898,946,1056 'entity-specif':270 'entityattributesaveda':244 'entitynotfounderror':721,1067 'enumer':507 'erd':136 'error':647,649,658,714,716,725,742,1062,1075 'et':245 'everi':165 'exclud':1019 'exclusivestartkey':921 'exist':638,685,705,708,1034,1043 'expertis':108 'export':566 'factori':968 'fals':424,639,1035 'field':1004 'filter':796,832,909 'first':457,920 'fixtur':966 'flexibl':125 'fluid':97 'fork':276 'formatteditem':559,578,1022 'fromrecord':587 'function':849,865 'get':659,663 'getitemcommand':604,674,1048 'global':207,220,233 'graphql':109 'gsi':154,747 'gsi1':205,260,762,785 'gsi1pk':210,363 'gsi1sk':215,369 'gsi2':218,269 'gsi2pk':223 'gsi2sk':228 'gsi3':231,277,449,905 'gsi3pk':236,458 'gsi3sk':241,464,471 'gsi4':827 'gsis':23 'handl':956 'hierarch':278 'hierarchi':541 'hoc':102 'id':516,534,538,540,1079 'identifi':536,544 'implement':26,130 'import':179,296,303,310,555,602,749 'includ':1025 'index':204,250,252,784,826,904 'inputitem':557,573,1016 'instanceof':650,717 'interact':48 'invert':115 'issu':263,496,497,512,519,757,770,781,812,823,830,835 'issue.fromrecord':791,842 'item':304,326,387,632,699,790,792,841,843,930,933,935,978 'json.parse':875 'json.stringify':860 'key':20,294,332,342,344,351,358,401,416,429,440,474,530,661,675,735,983,986,992,1000,1050 'known':68,121 'lack':106 'lastevalu':851,858,861 'layer':34 'limit':888,927 'link':293,352,359,365,371,430,441,460,996 'list':164,528,756,798 'listissu':764 'listopenissu':806 'listreposbyown':885 'local':963,1095 'lt':907 'main':254 'map':146,789,840,932 'mark':982 'member':266 'methodolog':175 'microservic':80 'model':127 'multi':945 'multi-ent':944 'must':707 'name':190,192,195,200,209,214,222,227,235,240,321,382,403,433,437,444,448,490,493,1010,1014 'need':72,99,169,249 'new':188,319,380,467,653,720 'newest':456,919 'nextoffset':936 'num':501,514 'offset':890,923 'open':831,834 'option':338,419,634,701,916 'order':551 'org':268 'owner':285,388,431,435,442,446,455,461,463,489,492,498,510,524,765,782,807,824,886,903 'pad':500,513 'pagin':27,844,847,883 'paradigm':118 'parent':537 'partit':780,822,901 'partitionkey':194,208,221,234 'pattern':21,66,113,123,151,159,163,291,475,529,531,598,600,743,745 'per':1080 'perform':77 'pk':196,349,427,477,504,637,704,1033,1042 'pk/sk':150,256 'pk/sk/gsi':343,999 'plan':156 'predict':73 'prefix':549 'prevent':640 'primari':257 'privat':421,613 'process.env.table':191 'project':37 'promis':624,666,691,731,769,811 'prototyp':95 'purpos':251,253,479 'putitemcommand':603,631,698,1030,1039 'puttransact':948 'queri':25,104,126,166,262,273,279,744,746,779,793,821,881,900,1061 'querycommand':750,776,818,897,1054 'quick':974 'rang':795,828,906 'read':580,1024,1052 'record':588,852,869 'refer':975 'references/entity-layer.md':594,595 'references/error-handling.md':739,740 'references/modeling.md':171,172 'references/testing.md':959,960 'references/transactions.md':941,942 'relat':117 'relationship':134,141,275 'repo':265,283,383,402,432,434,436,443,445,447,453,488,491,499,509,511,525,912 'repo.fromrecord':934 'repoent':379 'reponam':767,783,809,825 'repositori':487,599,760,1028 'requir':98,331,335,390,405 'resolv':110 'result':627,670,694,772,814,893 'result.item':679,681 'result.items':788,839,931 'result.lastevaluatedkey':938 'result.toolboxitem':645,712 'return':643,678,710,787,838,857,873,929 'revers':917 'rule':532 'run':1082 'safe':46,1060 'safeti':553 'saveda':470,1007 'scale':79 'schema':325,340,386,426,976,980,1013 'secondari':157 'see':170,593,738,940,958 'send':642,677,709,737,786,837,928 'separ':987 'setup':964 'simpl':535 'singl':7,56,90 'single-t':6,55,89 'sk':201,356,438,478,804 'skill' 'skill-typescript-dynamodb-toolbox' 'sort':282,452,548,972 'sortkey':199,213,226,239 'source-martinffx' 'specif':272 'stabl':71 'star':520,523 'static':586 'status':800 'strategi':155 'string':198,203,212,217,225,230,238,243,311,330,334,337,350,357,364,370,389,393,404,408,418,428,439,459,465,665,730,766,768,808,810,853,855,868,870,887,891 'sub':75 'sub-10ms':74 'syntax':289 'tabl':8,52,57,91,176,180,189,255,323,384 'team':105 'tempor':281,451,547,971 'test':957,965,973,1076,1081,1088,1092 'this.entity':629,672,696 'this.entity.build':733 'this.issueentity':778,820 'this.repoentity':899 'this.table':774,816,895 'throw':652,657,719,724 'timestamp':526,546,1084 'timestamp-bas':1083 'toisostr':469 'token':848,867,874,877 'toolbox':4,13,42,184,301,308,315,563,609,754 'topic-agent-skills' 'topic-agentic-coding' 'topic-anthropic' 'topic-claude-code' 'topic-claude-skills' 'topic-code-review' 'topic-codex' 'topic-codex-skill' 'topic-opencode' 'topic-prompt-engineering' 'topic-sdd' 'topic-spec-driven-development' 'torecord':591 'tostr':862,879 'transact':939,947 'transactioncanceledexcept':955 'transform':597 'tri':625,692 'true':706,918,1044 'type':45,197,202,206,211,216,219,224,229,232,237,242,542,552,556,558,565,567,571,576,1015,1059 'type-saf':44,1058 'typeof':569 'typescript':2,36,178,295,377,554,601,748,797,845 'typescript-dynamodb-toolbox':1 'undefin':668,682,856,864,872,880 'uniqu':1078 'unknown':854,871 'updat':683,688,1046,1068 'upfront':69 'usag':581 'use':10,15,61,63,761,801,977,993,995,1001,1006,1029,1038,1047,1053,1077,1093 'user':322,480,585,590,622,623,655,667,689,690,722 'user.fromrecord':644,680,711 'user.torecord':633,700 'user.username':656,723 'userent':318,570 'userformat':577,589 'userinput':572,592 'usernam':329,353,355,360,362,366,368,372,374,482,484,522,664,676,729,736 'userrecord':568,615 'userrepositori':611 'v2':14,43,288 'valid':376,391,406,1002,1005 'valu':392,400,407,415 'will':85 'work':29 'write':24,575,1018 'z0':397,412 'za':396,411","prices":[{"id":"37def149-6c22-4582-983d-c1b1f313bae5","listingId":"49e43b9e-3fc3-4e16-ad25-740485a29d87","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"martinffx","category":"atelier","install_from":"skills.sh"},"createdAt":"2026-05-10T07:03:13.517Z"}],"sources":[{"listingId":"49e43b9e-3fc3-4e16-ad25-740485a29d87","source":"github","sourceId":"martinffx/atelier/typescript-dynamodb-toolbox","sourceUrl":"https://github.com/martinffx/atelier/tree/main/skills/typescript-dynamodb-toolbox","isPrimary":false,"firstSeenAt":"2026-05-10T07:03:13.517Z","lastSeenAt":"2026-05-18T19:05:25.023Z"}],"details":{"listingId":"49e43b9e-3fc3-4e16-ad25-740485a29d87","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"martinffx","slug":"typescript-dynamodb-toolbox","github":{"repo":"martinffx/atelier","stars":23,"topics":["agent-skills","agentic-coding","anthropic","claude-code","claude-skills","code-review","codex","codex-skill","opencode","prompt-engineering","sdd","spec-driven-development"],"license":"mit","html_url":"https://github.com/martinffx/atelier","pushed_at":"2026-05-18T06:56:45Z","description":"An atelier for Opencode, Claude Code, and other coding agents: spec-driven workflows, deep thinking, and code quality.","skill_md_sha":"34c361e67ec4fcd431191391dd5186d9bf5ce822","skill_md_path":"skills/typescript-dynamodb-toolbox/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/martinffx/atelier/tree/main/skills/typescript-dynamodb-toolbox"},"layout":"multi","source":"github","category":"atelier","frontmatter":{"name":"typescript-dynamodb-toolbox","description":"DynamoDB single-table design using dynamodb-toolbox v2. Use when creating entities, defining key patterns, designing GSIs, writing queries, implementing pagination, or working with any DynamoDB data layer in TypeScript projects."},"skills_sh_url":"https://skills.sh/martinffx/atelier/typescript-dynamodb-toolbox"},"updatedAt":"2026-05-18T19:05:25.023Z"}}