AWS
DynamoDB
AppSync
GraphQL
Database Queries

Query DynamoDB with multiple begins_with clause in AppSync

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Amazon DynamoDB is a fully managed NoSQL database service that delivers high performance at any scale. Often used in serverless applications, DynamoDB is integrated with AWS AppSync, a managed service that uses GraphQL to make data queries simple and efficient. A unique challenge arises when you want to query DynamoDB using multiple begins_with conditions through AppSync. This article explores different strategies to tackle this, while offering technical insights and practical examples.

Understanding DynamoDB's Query Capabilities

DynamoDB is optimized for fast data retrieval and supports operations like GetItem, PutItem, and Query. The Query operation is particularly powerful but has certain limitations:

  • Query can retrieve items based on primary key values and sort key ranges.
  • It supports condition expressions like begins_with, between, and more.
  • Conditional queries are only applicable to attributes that are defined as keys.

AppSync and GraphQL

AWS AppSync provides a flexible GraphQL API to access DynamoDB tables. The GraphQL queries are translated into DynamoDB actions through resolvers. However, GraphQL's capabilities for filtering data on the server side are limited by the underlying database operations.

Implementing Multiple begins_with Conditions

Implementing multiple begins_with conditions directly in AppSync is not straightforward due to the constraints imposed by DynamoDB. Here are some effective strategies:

Composite Sort Keys

The most efficient way to implement multiple begins_with conditions is using composite sort keys. Here's how it works:

  1. Design your table: Use a composite sort key. For example, if you need to filter city and year, set the sort key as city_year.
  2. Query with a single begins_with: This is possible because you can format your query to use the compound logic you've encoded in the composite key:
graphql
1   {
2     queryInvoices(instituteId: "ABC123", beginsWithCondition: "NYC_202") {
3       items {
4         id
5         amount
6         date
7       }
8     }
9   }
  1. Resolver Mapping Template: Adjust your VTL (Velocity Template Language) to support the composite sort key:
vtl
1   {
2     "version" : "2018-05-29",
3     "operation" : "Query",
4     "query" : {
5        "expression": "begins_with(#sortKey, :sortVal)",
6        "expressionNames": {
7            "#sortKey": "city_year"
8        },
9        "expressionValues": {
10            ":sortVal": { "S": "${ctx.args.beginsWithCondition}" }
11        }
12     },
13     "consistentRead" : false
14   }

Client-side Filtering

If redesigning your table schema is not feasible, consider client-side filtering. This method is less efficient but effective when you deal with smaller datasets:

  1. Use a broad begins_with operation that encompasses all potential records.
  2. Post-process the results in your client application.
javascript
const allResults = await queryDynamoDB("begins_with": "NYC_");
const filteredResults = allResults.Items.filter(item => item.sortKey.startsWith("202"));

Lambda Resolvers

When business logic is too complex for native AppSync resolvers, AWS Lambda functions can act as resolvers:

  1. Set up a Lambda function to process the entire query.
  2. Within the Lambda, perform complex operations, including multiple begins_with conditions.
javascript
1exports.handler = async (event) => {
2  const { instituteId, city, year } = event.arguments;
3  const params = {
4    TableName: "Invoices",
5    KeyConditionExpression: "instituteId = :instituteId AND begins_with(city_year, :cityYear)",
6    ExpressionAttributeValues: {
7      ":instituteId": { "S": instituteId },
8      ":cityYear": { "S": `${city}_${year}` },
9    },
10  };
11
12  // Query DynamoDB and handle response
13  const result = await dynamoDBClient.query(params).promise();
14  return result.Items;
15};

Summary Table

Here's a summary of the different strategies:

StrategyUse CaseEfficiencyComplexityClient-Side Required
Composite Sort KeysWhen data is naturally compound in natureHighMediumNo
Client-side FilteringSmall datasets or prototypingLowLowYes
Lambda ResolversComplex business logic beyond basic filtersVariableHighNo

Conclusion

Leveraging multiple begins_with conditions in AWS AppSync when querying DynamoDB requires creative approaches due to inherent constraints. By using composite sort keys or Lambda resolvers, you can achieve efficient querying even with complex schema requirements. Evaluate your application's requirements and choose a strategy that best balances performance and complexity.


Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track 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.

Practice system design

All Rights Reserved.