DynamoDB GetItem in Node.js (AWS SDK v3)

AWS SDK v3 gives you two ways to read one item: GetItemCommand on a DynamoDBClient, which speaks the wire format ({S: '...'}), or GetCommand on a DynamoDBDocumentClient, which takes and returns plain JavaScript.

The example uses the low-level client. Those wrappers are what the attribute-value encoding actually looks like on the wire, and what error messages quote back at you. Either way the request needs the full primary key.

Code

import {DynamoDBClient, GetItemCommand} from '@aws-sdk/client-dynamodb';

const client = new DynamoDBClient({region: 'us-east-1'});

const command = new GetItemCommand({
  TableName: 'Music',
  Key: {
    Artist: {S: 'Arturo Sandoval'},
    SongTitle: {S: 'Cubano Chant'}
  },
  ProjectionExpression: '#proj0, #proj1, #proj2, #proj3',
  ExpressionAttributeNames: {
    '#proj0': 'Artist',
    '#proj1': 'SongTitle',
    '#proj2': 'AlbumTitle',
    '#proj3': 'Year'
  }
});

const response = await client.send(command);

if (!response.Item) {
  console.log('Item not found');
} else {
  console.log(response.Item);
}

Explanation

  • send(command), not client.getItem()DynamoDBClient exposes only send. The aggregated DynamoDB class from the same package does carry a getItem method if you want SDK-v2-style calls, at the price of pulling every command into your bundle.
  • A miss is undefined, not an errorresponse.Item is simply absent, and the call still resolves. response.$metadata always arrives, so truthiness on the response itself tells you nothing.
  • unmarshall picks the number type by magnitude — an {N: …} inside the safe-integer range comes back as a number, anything outside it as a BigInt, and a large non-integer throws can't be converted to BigInt. Pass {wrapNumbers: true} to unmarshall from @aws-sdk/util-dynamodb and every number arrives as a NumberValue instead, so you decide the conversion.
  • The #proj aliases are load-bearingYear is on AWS's reserved-word list, so a ProjectionExpression naming it directly is rejected. Aliasing every name, as above, is the safe default. It trims the response, not the read cost (why).
  • ConsumedCapacity is opt-in — add ReturnConsumedCapacity: 'TOTAL' and the response reports what this read actually cost: 0.5 capacity units for an eventually consistent read of an item under 4 KB, 1.0 once you add ConsistentRead: true (the trade-off).
  • Hoist the client — construct DynamoDBClient once at module scope. Building one per request, or inside a Lambda handler, throws away the connection pool and the resolved credentials on every call.

Do it visually

DynoTable shows items as ordinary rows rather than attribute-value maps, and exports the query behind the grid as a runnable SDK v3 program. Download DynoTable.

References

Last verified 2026-07-28 against the official AWS documentation linked above.

Work with DynamoDB without the Console

A fast DynamoDB desktop client that runs the real SQL DynamoDB can’t — JOINs, GROUP BY, aggregates — with visual editing and an AI agent on your own Bedrock keys.

Free 30-day trial, no credit card — then the Free plan with no time limit.