DynamoDB Rust: AWS SDK Query and CRUD Cheat Sheet

Written by Chris Biscardi
Last updated: October 23rd, 2026
Use aws-sdk-dynamodb with aws-config and Tokio to call DynamoDB from Rust. Configure the client with BehaviorVersion::latest(), build requests with the fluent API, and use into_paginator() for complete Query and Scan results. The SDK exposes typed AttributeValue maps; optional serde_dynamo conversion lets application code use Rust structs.
Table of contents
- Setup
- Create table
- Delete table
- List tables
- Get all items / Scan
- Get item
- Batch get item
- Put item
- Batch write / put item
- Query for a set of items
- Query an index
- Update item
- Conditionally update item
- Increment an item attribute
- Delete item
- Delete all items
- Simple transaction
- Read transaction
- Query with sorting
- Query and Scan pagination
- Handle SdkError and service errors
Setup
Create a project and add these dependencies to Cargo.toml. The versions checked for this refresh are aws-sdk-dynamodb 1.134.0 and aws-config 1.13.0; both declare Rust 1.94.1 as their minimum supported Rust version. Keep your application's Cargo.lock so later compatible releases do not silently change a deployment.
The AWS SDK crate reference documents its types and operations. aws-config supplies the credential and Region provider chains. Use AWS_PROFILE and AWS_REGION locally, or the workload's IAM role on AWS; never embed real credentials.
Put this first block in src/main.rs, then add the remaining Rust functions below it. The shared imports cover the whole collection. The entry point constructs a client without making writes; call only the operation you want to test. Run cargo check to check the combined example.
Every example uses my-table with String partition key name and Number sort key createdAt. Dates are numeric YYYYMMDD examples. GSI1 uses String pokemon_type and Number createdAt. This lets the same sample items support both individual lookups and type-based queries.
The optional serde_dynamo project is unarchived and its published 4.3.0 release supports AWS SDK 1.x through the aws-sdk-dynamodb+1 feature. Its latest published release was September 2025, so treat it as a third-party dependency rather than an AWS support commitment. The integration below is compatible with the SDK versions shown here; low-level examples work without a serializer.
Create table
Define only the attributes used as table or index keys. Required-field builders such as AttributeDefinition and KeySchemaElement return a Result; propagate build errors with ?. Creation is asynchronous, so use the SDK waiter instead of assuming a successful CreateTable response means the table is active.
The waiter requires the client::Waiters trait import. Its duration is an application wait budget, not an AWS completion guarantee. A failed wait does not necessarily mean table creation failed; inspect the table state before retrying creation.
Delete table
Deleting a table also deletes its items and indexes. Wait for absence when a test needs to reuse the table name. Do not call this function during normal startup.
List tables
List names in the configured account and Region with a paginator. A single ListTables call is only one page. The paginator's stream yields a result for every page; propagate failures rather than silently returning a partial list.
Get all items / Scan
A Scan evaluates the table's items. Consume pages as they arrive to keep memory bounded. Here the output accessor returns an empty slice when no items were returned, so there is no need to unwrap an optional collection.
For a filter, set the expression and its aliases before converting the request into a paginator:
The optional lengthInSeconds Number is not in the basic sample struct. Items missing it will not match this filter. Filtering occurs after reads, so it reduces returned data, not the read capacity already consumed. A filtered page may be empty while the paginator still has more work.
Get item
A single-item read needs both primary-key fields. A missing item yields None, not a missing-table error. consistent_read(true) requests a strongly consistent base-table read.
The typed version uses serde_dynamo::from_item. Required struct fields must exist with compatible types; #[serde(default)] allows score to be absent. Do not convert high-precision DynamoDB decimals blindly to f64; choose a representation that preserves your application's precision.
Batch get item
BatchGet retrieves known keys, including from multiple tables, with up to 100 items and a 16 MB response. Keys must be distinct. The results are unordered and missing items are omitted.
A successful response can contain UnprocessedKeys. This loop accumulates completed reads and retries only the remaining keys. The bounded retry count and delay are example policy choices; use a job-level deadline in production.
Add another table-name entry to the request map for multiple tables, then collect each table's response separately. A durable worker should return or persist partial results and pending keys when its retry budget expires; this small example reports failure instead of claiming completion.
Put item
A put creates or replaces the entire item at its complete key. The low-level representation stores numbers in AttributeValue::N as decimal strings. These are DynamoDB Numbers, not String attributes.
The second function serializes the same schema with serde_dynamo::to_item. #[serde(rename = "createdAt")] maps the idiomatic Rust field name to the existing DynamoDB attribute. Use condition_expression("attribute_not_exists(#name)") with a #name alias if an existing item must not be overwritten.
Batch write / put item
BatchWrite supports up to 25 puts/deletes and a 16 MB request. It does not support partial updates or conditions, and the overall batch is not atomic. A successful response can contain UnprocessedItems; those operations have not yet succeeded.
To include a delete, construct a DeleteRequest with the complete key and use WriteRequest::builder().delete_request(...). Do not put and delete the same key in one batch. Application retries should preserve the outstanding subset so they do not overwrite newer changes with an already completed put.
Query for a set of items
A Query selects equality on the partition key and optionally a sort-key condition. This reads snapshots of bulbasaur after a numeric date. The aliases and placeholder values match exactly. Use a paginator because one call is not guaranteed to return all matches.
A filter can further restrict non-key attributes, but is applied after the key-matched items have been read. The Query vs. Scan guide explains when to model another key-based access pattern rather than scanning.
Query an index
Use the index name and its keys. GSI1 finds all grass-type records by date, regardless of their base-table partition keys. A GSI supports eventual consistency only; do not request strong consistency. This index projects all attributes, so typed decoding has the fields it needs.
For an LSI, supply its index name and its sort-key condition while keeping the table's partition key. A GSI query cannot fetch attributes that were not projected into the index.
Update item
An update changes only selected attributes and can create the item when it is absent. The following example updates a non-key nickname. It cannot rename the primary-key attribute's value in place. Request AllNew to return the resulting item.
Conditionally update item
A conditional update checks the current item atomically with the mutation. This changes the nickname only when the item is a grass type and has not been soft-deleted. It fails instead of creating a partial item when the record is absent.
Handle a condition failure as a conflict or a business-rule outcome. Repeating the identical condition without changing state usually will not help. Error handling is shown below.
Increment an item attribute
Use an atomic update instead of a read-modify-write cycle. if_not_exists initializes a missing counter to zero. An atomic increment is not idempotent: retrying an uncertain application request can increment twice, so use an idempotency design for business-critical counts.
Delete item
Delete by the same complete key used for GetItem. An absent item is not an error. Add a condition if deletion depends on current state; ReturnValue::AllOld can return the removed attributes.
Delete all items
DynamoDB has no table-wide delete operation. Scan the primary keys and delete each item, or group them into batch deletes with unprocessed-item retries. This deliberately sequential example bounds concurrency and projects only the two key attributes. Stop concurrent writers when the goal is an empty table; a scan is not a snapshot.
Simple transaction
Transactions make multiple writes succeed or fail together. This cart example uses the same table schema: seed a stock item at stock#bulbasaur / 0 with numeric count, then atomically decrement it and insert a distinct reservation at cart#123#bulbasaur / 20261023. The condition prevents negative stock and a duplicate reservation.
Give each logical reservation a unique token and reuse it only for identical retries. The idempotency window lasts 10 minutes after completion. Transactions accept up to 100 unique items and 4 MB in one account and Region, with one action per item. MRSC global tables do not support transactions. Stock and cart records do not have the fields needed for Pokemon decoding, so scan them as raw maps or define separate Rust types.
Read transaction
A read transaction fetches specified keys atomically. It can span tables in the same account and Region, and its response order matches request order. It is not a transactional table scan.
Query with sorting
Query orders results by the queried sort key. Numbers sort numerically, strings by UTF-8 bytes. Set scan_index_forward(false) for descending order, such as newest snapshots first. There is no arbitrary server-side sort field for Scan.
Query and Scan pagination
Both Query and Scan evaluate up to 1 MB before filtering. A limit also counts evaluated items, not matching results. The into_paginator() examples continue automatically, including after empty filtered pages.
For a user-facing page, return the last_evaluated_key from one request and pass the complete map as exclusive_start_key on the next. Preserve every returned key component, including index attributes for a GSI cursor. Pagination details explain why item count is not a reliable completion signal.
When implementing a manual loop, stop when the key map is absent or empty, not merely when items is absent. Bind an externally supplied cursor to the same authorized query and key conditions.
Handle SdkError and service errors
SDK operations return SdkError<OperationError>, which can represent a service response, a timeout, a connection failure or a request-construction failure. Inspect the service error before treating a failure as a condition conflict. into_service_error() consumes the wrapper; for a non-service failure it produces the operation's unhandled error, so retain the original wrapper when transport details matter.
Use the operation-specific error enum, such as PutItemError, rather than the retired preview SDK's ErrorKind structs. The SDK handles configured request retries, but your application must handle final errors and successful batch responses containing unprocessed work. AWS's Rust DynamoDB examples provide further operation-specific examples.
Frequently Asked Questions
Which crates do I need for DynamoDB in Rust?
aws-sdk-dynamodb, aws-config and an async runtime such as Tokio. Optional serde_dynamo converts Serde structs to and from DynamoDB attribute maps.How do I paginate a DynamoDB query in Rust?
into_paginator().send(), then consume each page with next().await. Propagate errors and continue through empty filtered pages.Does Rust have a DynamoDB DocumentClient?
AttributeValue maps. The third-party serde_dynamo crate adds struct serialization with its aws-sdk-dynamodb+1 feature; it does not replace the AWS client.Why does build need a question mark in current Rust SDK examples?
Result. Propagate that build error with ? before passing the completed value into a request; some other builders return their value directly.How do I handle a conditional-write failure in Rust?
SdkError and check for ConditionalCheckFailedException. Keep timeouts and connection failures separate from a condition that did not match.Dynobase is a Professional GUI Client for DynamoDB
Start your 7-day free trial today