Back to Blog
Lesson 36 of the AWS: AWS Core Services for Developers course
Cloud NativeAugust 13, 20264 min read

Advanced DynamoDB Query Patterns: Using GSIs for Optimization

Master DynamoDB query patterns by using Global Secondary Indexes (GSIs). Learn how to optimize table design for flexible data retrieval in your serverless apps.

AWSDynamoDBServerlessCloud NativeBackendGSI
A close-up view of a laptop displaying a search engine page.

Previously in this course, we covered CRUD Operations with AWS SDK and Connecting Lambda to DynamoDB. While those lessons focused on single-item lookups via Partition Keys, real-world apps often require searching by attributes that aren't your primary key. This lesson adds DynamoDB Global Secondary Indexes (GSIs) to your toolkit, allowing for efficient Query Optimization without scanning your entire database.

Understanding the GSI First Principles

In DynamoDB, the primary key design dictates how you retrieve data. If you have a Users table with a UserId as the partition key, you can easily fetch a user by ID. But what if you need to find all users by their Email?

Without a GSI, you would be forced to use a Scan operation—which reads every single item in the table, consuming massive amounts of Read Capacity Units (RCUs) and latency. A GSI solves this by creating a "shadow" table that automatically syncs with your main table, but allows you to define a completely different primary key for that index.

Designing a Global Secondary Index

When you create a GSI, you are essentially telling DynamoDB: "Keep a copy of these items, but index them by this other attribute."

FeatureBase TableGlobal Secondary Index (GSI)
Primary KeyMust be uniqueCan have duplicates
SyncN/AAsynchronous (eventually consistent)
CapacityProvisioned/On-DemandIndependent from Base Table
ProjectionAll AttributesChoose: KeysOnly, Include, or All

Worked Example: Indexing by Email

Let's assume our web app project needs to look up users by Email. In our CDK stack (from our previous work in Project Integration: Exposing the API with AWS CDK), we define the GSI when creating the table resource:

TYPESCRIPT
const userTable = new dynamodb.Table(this, CE9178">'UserTable', {
  partitionKey: { name: CE9178">'userId', type: dynamodb.AttributeType.STRING },
  billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});

// Adding the GSI
userTable.addGlobalSecondaryIndex({
  indexName: CE9178">'EmailIndex',
  partitionKey: { name: CE9178">'email', type: dynamodb.AttributeType.STRING },
  projectionType: dynamodb.ProjectionType.ALL, // Or use INCLUDE for specific fields
});

To query using this index in your Lambda function, you specify the IndexName in your SDK call:

JAVASCRIPT
const params = {
  TableName: CE9178">'UserTable',
  IndexName: CE9178">'EmailIndex', // Important: Use the index name
  KeyConditionExpression: CE9178">'email = :emailValue',
  ExpressionAttributeValues: {
    CE9178">':emailValue': CE9178">'dev@example.com'
  }
};

const result = await docClient.send(new QueryCommand(params));

Hands-on Exercise

  1. Modify your CDK stack: Update your existing DynamoDB table definition to include a GSI on an attribute relevant to your project (e.g., createdAt or status).
  2. Deploy the stack: Run cdk deploy to provision the index.
  3. Query the index: Write a small Node.js script (or a new Lambda function) that uses the QueryCommand to retrieve items using your new index instead of the base table's partition key.

Common Pitfalls

  • Over-indexing: GSIs cost money to store and write to. Don't create an index for every attribute; only create them for patterns you actually use in production.
  • Assuming Strong Consistency: GSIs are eventually consistent. If you write an item and immediately query the GSI, the item might not show up yet. Design your application logic to handle this micro-delay.
  • Projection Bloat: If you set ProjectionType to ALL, the GSI consumes more storage and write capacity because it copies every attribute. If you only need to verify a user exists, use KEYS_ONLY to keep the index lean.

FAQ

Can I delete a GSI later? Yes, you can remove a GSI via CDK or the console, but remember that the index's storage is reclaimed and you will lose that query pattern immediately.

Does a GSI slow down writes to my main table? Writing to the base table is slightly more expensive in terms of Write Capacity Units (WCUs) because DynamoDB must perform an additional write to update the GSI. However, it is an asynchronous process that doesn't block your main application flow.

What if I need to query by two attributes (e.g., Email AND Status)? A standard GSI supports a Partition Key and an optional Sort Key. You could set Email as the GSI Partition Key and Status as the GSI Sort Key to query by both simultaneously.

Recap

We've moved beyond simple primary key lookups by implementing DynamoDB Global Secondary Indexes. By leveraging GSIs, you can keep your Query Optimization strategy efficient, allowing your application to scale without the performance penalties of full table scans. Remember to keep your index projections lean and be mindful of the eventual consistency model.

Up next: We will implement API Throttling to protect your backend from unexpected traffic spikes.

Similar Posts