Expand description
A typed facade over aws-sdk-dynamodb with composable
expression builders and typestate operation builders.
dynamodb-facade eliminates the boilerplate of raw DynamoDB calls —
manual key maps, expression strings, placeholder tracking, pagination loops,
and batch-write chunking — while enforcing correct usage at compile time
through Rust’s type system.
§Key Concepts
§Tables, items, and the TD parameter
The DynamoDBItem<TD> trait wires a Rust struct to a
TableDefinition, declaring how its fields map to DynamoDB key
attributes. The blanket traits DynamoDBItemOp<TD>,
DynamoDBItemBatchOp<TD>, and DynamoDBItemTransactOp<TD> are
automatically implemented for every type that implements DynamoDBItem,
providing get, put, delete, update, query, scan, batch_put,
batch_delete, transact_put, and friends as associated functions.
TD is deliberately a generic type parameter, not an associated type.
A single Rust struct can implement DynamoDBItem for multiple tables,
which is useful when:
- Multiple tables share domain types — for example, a
Userstruct that exists in both a primary table and an archive table, possibly with different key mappings. - Migration logic — reading items from one table and writing them to another, for one-shot migrations, compaction, or aggregation across tables.
§Mono-table (single-table) design
The crate has first-class support for the single-table pattern, where all
entity types share one DynamoDB table with a composite PK + SK key and
a type discriminator attribute (e.g. _TYPE). This is a natural fit
because the trait system already enforces per-entity key mappings, type
discriminators, and serialization — but it is not the only layout the
crate supports.
§Schema definitions
Attributes, tables, and indexes are declared as zero-sized types using the
attribute_definitions!, table_definitions!, and
index_definitions! macros. These types serve as compile-time tokens
that the library uses to build correct key maps and expression attribute
name/value maps without any runtime string manipulation by the caller.
They also encode key schema shape into the type system — for instance,
attempting to supply a sort key for a table declared with a partition key
only is a compile-time error.
§Expression builders
Condition<'a> and Update<'a> are composable value types that build
DynamoDB condition and update expressions. They support the full DynamoDB
expression language — comparisons, begins_with, contains, between,
IN, size, if_not_exists, list_append, set ADD/DELETE — and
compose with &, |, ! operators and .and() / .combine() methods.
All placeholder names and values are managed internally; callers never
touch #name or :value strings.
§Typestate operation builders
Every operation builder (GetItemRequest, PutItemRequest,
DeleteItemRequest, UpdateItemRequest, QueryRequest,
ScanRequest) uses compile-time typestate parameters to enforce correct
usage:
OutputFormat(Typed/Raw) — whether the terminal method deserializes intoTor returnsItem<TD>.ReturnValue(ReturnNothing/Return<Old>/Return<New>) — whether put/delete/update return item attributes.- Expression-set state (
NoCondition/AlreadyHasCondition, etc.) — calling.condition()or.filter()twice is a compile-time error.
§Quick Start
Define the schema, wire a struct, then perform CRUD operations:
use dynamodb_facade::{
attribute_definitions, table_definitions, index_definitions, dynamodb_item,
Condition, Update, KeyId, DynamoDBItemOp, DynamoDBError, Error,
StringAttribute, NumberAttribute, HasAttribute
};
use serde::{Deserialize, Serialize};
// 1. Declare attribute zero-sized types.
attribute_definitions! {
PK { "PK": StringAttribute }
SK { "SK": StringAttribute }
ItemType { "_TYPE": StringAttribute }
Email { "email": StringAttribute }
}
// 2. Declare the table.
table_definitions! {
PlatformTable {
type PartitionKey = PK;
type SortKey = SK;
fn table_name() -> String {
std::env::var("TABLE_NAME").expect("TABLE_NAME must be set")
}
}
}
// 3. Define an item type and wire it to the table.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: String,
pub name: String,
pub email: String,
}
dynamodb_item! {
#[table = PlatformTable]
User {
#[partition_key]
PK {
fn attribute_id(&self) -> &'id str { &self.id }
fn attribute_value(id) -> String { format!("USER#{id}") }
}
#[sort_key]
SK { const VALUE: &'static str = "PROFILE"; }
ItemType { const VALUE: &'static str = "USER"; }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Enrollment {
pub user_id: String,
pub course_id: String,
pub enrolled_at: u64,
pub progress: f64,
}
dynamodb_item! {
#[table = PlatformTable]
Enrollment {
#[partition_key]
PK {
fn attribute_id(&self) -> <User as HasAttribute<PK>>::Id<'id> {
&self.user_id
}
fn attribute_value(id) -> <User as HasAttribute<PK>>::Value {
<User as HasAttribute<PK>>::attribute_value(id)
}
}
#[sort_key]
SK {
fn attribute_id(&self) -> &'id str { &self.course_id }
fn attribute_value(id) -> String { format!("ENROLL#{id}") }
}
ItemType { const VALUE: &'static str = "ENROLLMENT"; }
}
}
// 4. CRUD — no boilerplate.
let user = User {
id: "u-1".to_owned(),
name: "Alice".to_owned(),
email: "alice@example.com".to_owned(),
};
// Put (create or overwrite):
user.put(client).await?;
// Put with create-only guard:
user.put(client).not_exists().await?;
// Get by ID:
let loaded /* : Option<User> */ = User::get(client, KeyId::pk("u-1")).await?;
// Update with condition:
User::update_by_id(
client,
KeyId::pk("u-1"),
Update::set("name", "Alicia"),
)
.exists()
.await?;
// Delete:
User::delete_by_id(client, KeyId::pk("u-1")).await?;§Feature Highlights
§Composable conditions
// Attribute-level existence checks:
let c = Condition::exists("email") & Condition::not_exists("deleted_at");
// Item-level existence (uses the table's PK attribute):
let c = User::exists() & Condition::eq("role", "student");
// OR / NOT:
let c = User::not_exists() | Condition::lt("expiration_timestamp", 9999999999u64);
let c = !Condition::eq("status", "archived");
// Variadic AND over a collection:
let c = Condition::and([
Condition::eq("role", "instructor"),
Condition::size_gt("bio", 0),
Condition::exists("verified_at"),
]);§Composable updates
// Simple set / remove:
let u = Update::set("name", "Alice").and(Update::remove("legacy_field"));
// Atomic counters:
let u = Update::increment("login_count", 1);
let u = Update::init_increment("enrollment_count", 0, 1); // if_not_exists + increment
// Merge optional updates from an iterator:
let new_name: Option<&str> = Some("Alice");
let new_role: Option<&str> = None;
let u = Update::combine(
[
new_name.map(|n| Update::set("name", n)),
new_role.map(|r| Update::set("role", r)),
]
.into_iter()
.flatten(),
);§Query and scan with automatic pagination
// Query all enrollments for a user (auto-paginates):
let enrollments: Vec<Enrollment> =
Enrollment::query(client, Enrollment::key_condition("user-1"))
.all()
.await?;
// Query a GSI:
let users: Vec<User> =
User::query_index::<EmailIndex>(client, KeyCondition::pk("alice@example.com"))
.all()
.await?;
// Scan with a filter (note: from a pure DynamoDB stand point you should never do that):
let instructors: Vec<User> = User::scan(client)
.filter(Condition::eq("role", "instructor"))
.all()
.await?;§Batch writes
// Automatically chunks into 25-item batches, runs in parallel,
// and retries unprocessed items:
let requests: Vec<_> = enrollments.iter().map(|e| e.batch_put()).collect();
dynamodb_batch_write::<PlatformTable>(client, requests).await?;§Transactions
// Atomically create an enrollment and increment the user's enrollment count:
client
.transact_write_items()
.transact_items(enrollment.transact_put().not_exists().build())
.transact_items(
User::transact_update_by_id(
KeyId::pk("user-1"),
Update::init_increment("enrollment_count", 0, 1),
)
.condition(
User::exists()
& Condition::lt("enrollment_count", 10u32),
)
.build(),
)
.send()
.await?;§Logical Module Organization
All items are re-exported from the crate root. The internal modules are:
schema—TableDefinition,IndexDefinition,KeySchema,AttributeDefinition,HasAttribute,HasConstAttribute, and the attribute type markers (StringAttribute,NumberAttribute,BinaryAttribute).item—DynamoDBItem<TD>,Item<TD>,Key<TD>,KeyId,NoId,KeyBuilder.expressions—Condition<'a>,Update<'a>,UpdateSetRhs<'a>,KeyCondition,Projection,Comparison.operations—GetItemRequest,PutItemRequest,DeleteItemRequest,UpdateItemRequest,QueryRequest,ScanRequest,DynamoDBItemOp,DynamoDBItemBatchOp,DynamoDBItemTransactOp, batch helpers (dynamodb_batch_write,batch_put,batch_delete), and pagination helpers (dynamodb_execute_query,dynamodb_stream_query,dynamodb_execute_scan,dynamodb_stream_scan).values—IntoAttributeValue,to_attribute_value,try_to_attribute_value,AsSet<T>,AsNumber<T>.error—Error,Result<T>.macros—attribute_definitions!,table_definitions!,index_definitions!,dynamodb_item!,has_attributes!,attr_list!,key_schema!.
§Error Handling
All fallible operations return Result<T> (an alias for
core::result::Result<T, Error>). The Error enum has five
variants:
Error::DynamoDB— wraps a boxedDynamoDBErrorfrom the AWS SDK. UseError::as_dynamodb_errorto downcast and match on specific SDK error types such asConditionalCheckFailedException.Error::Serde— aserde_dynamo(de)serialization failure.Error::FailedBatchWrite— a batch write that could not complete after all retry attempts. Contains the unprocessedWriteRequests.Error::Custom— a caller-supplied string message, created viaError::custom.Error::Other— anyBox<dyn Error + Send + Sync>, created viaError::other.
let user = sample_user();
// Override an existing item and retrieve the previous version.
// `.exists()` adds a condition that fails if the item is not already present.
match user.put(client).exists().return_old().await {
Ok(Some(old)) => { /* found old value */ }
Ok(None) => { unreachable!("condition fail if nothing to return") }
Err(err)
if matches!(
err.as_dynamodb_error(),
Some(DynamoDBError::ConditionalCheckFailedException(_))
) =>
{
println!("item did not exist yet — nothing was overwritten");
}
Err(err) => return Err(err),
}§Feature Flags
test-fixtures— exposes thetest_fixturesmodule outside ofcfg(test)andcfg(doc). Useful for integration test crates that want to reuse the domain types defined there.integration— gates integration tests that require a running DynamoDB Local instance (viatestcontainers). Not needed for normal library use.
Re-exports§
pub use aws_sdk_dynamodb;
Modules§
- test_
fixtures - Shared domain types for doc examples and integration tests.
Macros§
- attr_
list - Builds the nested tuple type used to represent a list of
AttributeDefinitiontypes. - attribute_
definitions - Declares one or more DynamoDB attribute definitions as zero-sized types.
- dynamodb_
item - Wires a Rust struct to a DynamoDB table by implementing
DynamoDBItemand the attribute traits. - has_
attributes - Manually implements
HasAttributeorHasConstAttributefor a type. - index_
definitions - Defines one or more DynamoDB Secondary Index (LSI or GSI) zero-sized types implementing
IndexDefinition. - key_
schema - Defines a key schema struct implementing
KeySchema. - table_
definitions - Defines one or more DynamoDB table zero-sized types implementing
TableDefinition.
Structs§
- AsNumber
- A generic newtype wrapper that converts any
T: Into<String>directly to a DynamoDBN(number) attribute value without parsing. - AsSet
- A newtype wrapper around
Vec<T>that serializes as a DynamoDB Set type (SS,NS, orBS) instead of a List (L). - Binary
Attribute - Marker type for DynamoDB Binary (
B) attributes. - Client
- Client for Amazon DynamoDB
- Composite
Key - Marker type indicating a key schema with both a partition key and a sort key.
- Condition
- Composable DynamoDB condition expression builder.
- Delete
Item Request - Builder for a DynamoDB
DeleteItemrequest. - GetItem
Request - Builder for a DynamoDB
GetItemrequest. - Item
- A type-safe wrapper around a DynamoDB item for a specific table.
- Key
- A type-safe wrapper for a DynamoDB key belonging to a specific table.
- KeyCondition
- Builder for DynamoDB key condition expressions.
- KeyId
- Type-safe builder for a DynamoDB partition key + sort key pair.
- NoId
- Zero-sized placeholder used when a key component is a compile-time constant.
- Number
Attribute - Marker type for DynamoDB Number (
N) attributes. - Projection
- Projection expression builder that automatically includes the table’s key attributes.
- PutItem
Request - Builder for a DynamoDB
PutItemrequest. - Query
Request - Builder for a DynamoDB
Queryrequest. - Scan
Request - Builder for a DynamoDB
Scanrequest. - Simple
Key - Marker type indicating a key schema with a partition key only (no sort key).
- String
Attribute - Marker type for DynamoDB String (
S) attributes. - Transact
Condition Check Request - Builder for a
ConditionCheckoperation inside a DynamoDB transaction. - Transact
Delete Request - Builder for a
Deleteoperation inside a DynamoDB transaction. - Transact
PutRequest - Builder for a
Putoperation inside a DynamoDB transaction. - Transact
Update Request - Builder for an
Updateoperation inside a DynamoDB transaction. - Update
- Composable DynamoDB update expression builder.
- Update
Item Request - Builder for a DynamoDB
UpdateItemrequest. - Update
SetRhs - Advanced right-hand-side expression builder for DynamoDB SET actions.
Enums§
- Attribute
Value Represents the data for an attribute.
Each attribute value is described as a name-value pair. The name is the data type, and the value is the data itself.
For more information, see Data Types in the Amazon DynamoDB Developer Guide.
- Comparison
- Comparison operators for DynamoDB condition and filter expressions.
- DynamoDB
Error - All possible error types for this service.
- Error
- The error type for all
dynamodb-facadeoperations.
Traits§
- Attribute
Definition - Defines the name and type of a single DynamoDB attribute at the type level.
- Attribute
List - A sealed, recursive tuple-list of additional DynamoDB attributes for an item.
- Attribute
Type - Sealed marker trait for DynamoDB attribute types.
- Composite
KeySchema - A
KeySchemawith both a partition key and a sort key. - DynamoDB
Item - Core trait for types stored in a DynamoDB table.
- DynamoDB
Item Batch Op - Entry points for building DynamoDB
BatchWriteItemwrite requests. - DynamoDB
Item Op - Primary entry point for typed single-item and collection CRUD operations.
- DynamoDB
Item Transact Op - Entry points for building DynamoDB
TransactWriteItemsoperations. - HasAttribute
- Links an item type to a dynamic DynamoDB attribute.
- HasConst
Attribute - Links an item type to a compile-time constant DynamoDB attribute value.
- Index
Definition - Defines a DynamoDB Global Secondary Index (GSI) or Local Secondary Index (GSI) on a specific table.
- Into
Attribute Value - Converts a Rust value into a DynamoDB
AttributeValue. - Into
Typed Attribute Value - A type-safe variant of
IntoAttributeValuethat guarantees the producedAttributeValuematches a specific DynamoDB scalar type. - KeyBuilder
- Builds DynamoDB keys from type-safe key IDs.
- KeyCondition
State - Sealed typestate marker for
KeyConditionbuild stages. - KeySchema
- Defines the key schema for a DynamoDB table or index.
- KeySchema
Kind - Sealed marker trait for key schema kinds.
- Simple
KeySchema - A
KeySchemawith only a partition key (no sort key). - Table
Definition - Defines a DynamoDB table: its name and key schema.
- Valid
KeySchema - Ensures that a
KeySchemaimplementation is internally consistent.
Functions§
- batch_
delete - Creates a
DeleteRequestWriteRequestfrom a rawKey. - batch_
put - Creates a
PutRequestWriteRequestfrom a rawItem. - dynamodb_
batch_ write - Executes a batch of
WriteRequests against a DynamoDB table. - dynamodb_
execute_ query - Executes a DynamoDB
Queryrequest, collecting all pages into aVec. - dynamodb_
execute_ scan - Executes a DynamoDB
Scanrequest, collecting all pages into aVec. - dynamodb_
stream_ query - Creates a lazy async
Streamof query results with automatic pagination. - dynamodb_
stream_ scan - Creates a lazy async
Streamof scan results with automatic pagination. - to_
attribute_ value - Converts a
serde::Serializevalue into a DynamoDBAttributeValueusingserde_dynamo. - try_
to_ attribute_ value - Converts a
serde::Serializevalue into a DynamoDBAttributeValueusingserde_dynamo, returning aResulton failure.
Type Aliases§
- Index
Schema - Convenience alias for the
KeySchemaof anIndexDefinition. - Result
- A specialized
Resulttype for this crate. - Table
Schema - Convenience alias for the
KeySchemaof aTableDefinition.