DynamoDB
dpl.DynamoDB(
table_name: str, # Table name in AWS
pk: str = "id", # Partition key
pk_type: str = "S", # "S" (String) | "N" (Number) | "B" (Binary)
sk: str = None, # Optional sort key
sk_type: str = "S", # "S" | "N" | "B"
gsi: list = None, # Global Secondary Indexes
billing_mode: str = "PAY_PER_REQUEST",# "PAY_PER_REQUEST" | "PROVISIONED"
read_capacity: int = None, # Only for PROVISIONED (default: 5)
write_capacity: int = None, # Only for PROVISIONED (default: 5)
ttl_attribute: str = None, # Time-To-Live attribute
stream: str = None, # "NEW_IMAGE" | "OLD_IMAGE" | "NEW_AND_OLD_IMAGES" | "KEYS_ONLY"
point_in_time_recovery: bool = False, # Enables PITR
sse_enabled: bool = True, # Encryption at rest with AWS-managed KMS
deletion_policy: str = "Delete", # "Delete" | "Retain" | "Snapshot"
existing: bool = False, # True = do not create, only inject env var
)
CloudFormation type
deployless always generates AWS::DynamoDB::Table regardless of whether a sort key or GSI is defined. This avoids CloudFormation replacement (and data loss) when you later add a sort key or GSI.
Auto-generated environment variable
The -table / _table suffix is removed to avoid redundancy:
table_name | Environment variable |
|---|---|
users-table | USERS_TABLE |
orders_table | ORDERS_TABLE |
sessions | SESSIONS_TABLE |
GSI format
Each element of the gsi list accepts:
{
"name": "StatusIndex", # Required — index name
"pk": "status", # Required — index partition key
"pk_type": "S", # Optional, default "S"
"sk": "created_at", # Optional — index sort key
"sk_type": "S", # Optional, default "S"
"projection": "ALL", # "ALL" | "KEYS_ONLY" | "INCLUDE" (default "ALL")
"non_key_attributes": ["email"], # Required only if projection="INCLUDE"
}
Examples
Simple table (PK only)
dpl.DynamoDB("sessions-table", pk="session_id", ttl_attribute="expires_at")
# → AWS::DynamoDB::Table
# → Variable: SESSIONS_TABLE
Table with SK and multiple GSIs
dpl.DynamoDB(
"orders-table",
pk="tenant_id",
sk="order_id",
gsi=[
{
"name": "StatusIndex",
"pk": "status",
"sk": "created_at",
},
{
"name": "CustomerIndex",
"pk": "customer_id",
"projection": "INCLUDE",
"non_key_attributes": ["total", "status"],
},
],
ttl_attribute="expires_at",
point_in_time_recovery=True,
deletion_policy="Retain",
)
# → AWS::DynamoDB::Table with SSEEnabled=True
# → Variable: ORDERS_TABLE
Provisioned capacity
dpl.DynamoDB(
"high-traffic-table",
pk="pk",
sk="sk",
billing_mode="PROVISIONED",
read_capacity=100,
write_capacity=50,
)
DynamoDB Streams
dpl.DynamoDB(
"events-table",
pk="event_id",
stream="NEW_AND_OLD_IMAGES", # Triggers a Lambda on every change
)
Existing table (do not create, only inject env var)
dpl.DynamoDB("prod-users-table", existing=True)
# Does not generate a CloudFormation resource
# Injects: PROD_USERS_TABLE = "prod-users-table" (literal string)
Permission levels
| Level | Auto-generated SAM policy |
|---|---|
"crud" (default) | DynamoDBCrudPolicy |
"read" | DynamoDBReadPolicy |
"write" | DynamoDBWritePolicy |
# Auto-detected resources get "crud" by default.
# Use resources={} only to restrict:
dpl.configure(
resources={
"catalog": (dpl.DynamoDB("catalog-table"), "read"),
},
)