DynamoDB get item TypeScript hell
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
Using DynamoDB's GetItem with TypeScript is frustrating because the AWS SDK returns items as Record<string, AttributeValue> — a deeply nested structure of typed wrappers like { S: "hello" } and { N: "42" } instead of plain values. TypeScript's strict typing exposes every awkward edge of this format. The solutions are to use the @aws-sdk/lib-dynamodb document client (which handles marshalling automatically), use @aws-sdk/util-dynamodb to unmarshall manually, or define custom type guards.
The Problem: Raw DynamoDB Types
Every field requires accessing a type-specific property (S for string, N for number, M for map), and numbers come back as strings that need parsing.
Solution 1: DynamoDB Document Client (Recommended)
The document client from @aws-sdk/lib-dynamodb automatically marshalls and unmarshalls DynamoDB's AttributeValue format. Keys and values use plain JavaScript types.
Solution 2: Manual Unmarshalling
unmarshall() converts { S: "hello" } to "hello", { N: "42" } to 42, etc. Use this when you need the raw client for specific features but want clean types for the result.
Solution 3: Type-Safe Wrapper Function
Handling Number Precision
DynamoDB stores numbers with up to 38 digits of precision. JavaScript's Number type loses precision beyond 2^53. Use wrapNumbers: true if your data contains large integers or high-precision decimals.
Handling Missing Items
GetItem does not throw an error for missing keys. It returns undefined for Item. Always check before casting.
Batch Get with Types
Common Pitfalls
- Using the raw
DynamoDBClientinstead ofDynamoDBDocumentClient: The raw client requires manually wrapping every value in{ S: ... },{ N: ... }, etc., and returns items in the same format. The document client handles this automatically and should be the default choice. - Forgetting that DynamoDB numbers are strings in the raw SDK:
{ N: "42" }contains a string, not a number. If you use the raw client, you must callparseInt()orparseFloat()on every numeric field. The document client converts these automatically. - Casting
result.Item as Twithout null checking:GetItemreturnsundefinedforItemwhen the key does not exist. Casting without checking leads to runtime errors. Always checkif (result.Item)first. - Losing precision with large numbers: JavaScript's
Number.MAX_SAFE_INTEGERis 2^53 - 1. DynamoDB supports 38-digit numbers. UsewrapNumbers: truein the document client to preserve precision for large values. - Mixing v2 and v3 SDK imports: The v2 SDK uses
AWS.DynamoDB.DocumentClient, while v3 uses@aws-sdk/lib-dynamodb. Mixing imports from both SDKs causes confusing type errors. Stick to one SDK version.
Summary
- Use
DynamoDBDocumentClientfrom@aws-sdk/lib-dynamodbto avoid the{ S: ... }/{ N: ... }wrapper hell - Cast
result.Item as YourTypefor TypeScript type safety after checking forundefined - Use
unmarshall()from@aws-sdk/util-dynamodbwhen you need the raw client but want plain types - Create a generic
getItem<T>()wrapper function for reusable, type-safe access - Handle missing items explicitly —
GetItemreturnsundefined, not an error - Enable
wrapNumbers: truefor high-precision numeric data
Related reading
- DynamoDB GSI BatchGetItem
- dynamodb how to increment a value in map
- dynamodb how to query by sort key only?
- dynamodb how to query by sort key only?
- DynamoDB how to use index in PartiQL queries?
- DynamoDB if_not_exists on UpdateItem
- ECMAScript 6 arrow function that returns an object
- ECONNREFUSED for Postgres on nodeJS with dockers

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.