DynamoDB
02 / 03

CRUD Operations & Queries

DynamoDB: CRUD Operations & Queries

AWS SDK v3 — Node.js

import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
import { DynamoDBDocumentClient, GetCommand, PutCommand, UpdateCommand,
         DeleteCommand, QueryCommand, ScanCommand, TransactWriteCommand } from '@aws-sdk/lib-dynamodb'

const client = new DynamoDBClient({ region: 'eu-west-1' })
const db = DynamoDBDocumentClient.from(client)
const TABLE = 'MyApp'

GetItem & PutItem

// GetItem — fetch by exact primary key
const { Item } = await db.send(new GetCommand({
  TableName: TABLE,
  Key: { PK: 'USER#alice', SK: 'PROFILE' },
  ConsistentRead: true,  // strong consistency
}))

// PutItem — create or overwrite
await db.send(new PutCommand({
  TableName: TABLE,
  Item: {
    PK: 'USER#alice',
    SK: 'PROFILE',
    name: 'Alice',
    email: 'alice@example.com',
    createdAt: new Date().toISOString(),
  },
  // Prevent overwrite if item already exists
  ConditionExpression: 'attribute_not_exists(PK)',
}))

// UpdateItem — modify specific attributes without overwriting whole item
await db.send(new UpdateCommand({
  TableName: TABLE,
  Key: { PK: 'USER#alice', SK: 'PROFILE' },
  UpdateExpression: 'SET #name = :name, updatedAt = :ts ADD loginCount :one',
  ExpressionAttributeNames: { '#name': 'name' },  // 'name' is a reserved word
  ExpressionAttributeValues: {
    ':name': 'Alice Smith',
    ':ts': new Date().toISOString(),
    ':one': 1,
  },
}))

Query & Scan

// Query — efficient, uses key conditions (PK required)
const { Items } = await db.send(new QueryCommand({
  TableName: TABLE,
  KeyConditionExpression: 'PK = :pk AND begins_with(SK, :prefix)',
  FilterExpression: '#status = :active',   // applied after key filter — still reads all matching keys
  ExpressionAttributeNames: { '#status': 'status' },
  ExpressionAttributeValues: {
    ':pk': 'USER#alice',
    ':prefix': 'ORDER#',
    ':active': 'active',
  },
  ScanIndexForward: false,  // DESC order by sort key
  Limit: 10,                // max items per page
}))

// Paginate — LastEvaluatedKey signals more pages exist
async function queryAll(pk: string) {
  const items = []
  let lastKey
  do {
    const { Items: page, LastEvaluatedKey } = await db.send(new QueryCommand({
      TableName: TABLE,
      KeyConditionExpression: 'PK = :pk',
      ExpressionAttributeValues: { ':pk': pk },
      ExclusiveStartKey: lastKey,
    }))
    items.push(...(page ?? []))
    lastKey = LastEvaluatedKey
  } while (lastKey)
  return items
}

// Scan — reads entire table (avoid in production unless necessary)
const { Items: all } = await db.send(new ScanCommand({
  TableName: TABLE,
  FilterExpression: 'contains(tags, :tag)',
  ExpressionAttributeValues: { ':tag': 'featured' },
}))

Transactions

// TransactWrite — all-or-nothing across up to 100 items in one table (or multiple)
await db.send(new TransactWriteCommand({
  TransactItems: [
    {
      Update: {
        TableName: TABLE,
        Key: { PK: 'PRODUCT#widget', SK: 'METADATA' },
        UpdateExpression: 'ADD stock :minus',
        ConditionExpression: 'stock >= :qty',
        ExpressionAttributeValues: { ':minus': -1, ':qty': 1 },
      }
    },
    {
      Put: {
        TableName: TABLE,
        Item: {
          PK: 'USER#alice',
          SK: 'ORDER#2026-999',
          productId: 'widget',
          qty: 1,
          status: 'pending',
        },
        ConditionExpression: 'attribute_not_exists(PK)',
      }
    }
  ]
}))

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free