DynamoDB ConditionExpression for Concurrency Control

Problem

Incrementing a download counter — concurrent requests can exceed the max limit. The GET → check → UPDATE pattern is vulnerable to race conditions.

Solution

import { UpdateCommand } from '@aws-sdk/lib-dynamodb';

await docClient.send(new UpdateCommand({
  TableName: 'tokens',
  Key: { PK: `TOKEN#${token}`, SK: 'TOKEN' },
  UpdateExpression: 'SET downloadCount = downloadCount + :inc',
  ConditionExpression: 'downloadCount < :max',
  ExpressionAttributeValues: {
    ':inc': 1,
    ':max': maxDownloads,
  },
}));
// Throws ConditionalCheckFailedException if condition fails

Key Points

  • ConditionExpression checks the condition before executing the update. If the condition fails, the update doesn’t execute. This is atomic — race conditions are impossible.
  • “Read → check → write” in application code allows other requests to slip in between. Delegating the check to DynamoDB eliminates that gap.
  • Catch ConditionalCheckFailedException to return “download limit exceeded” responses. This error is a normal part of business logic.