DynamoDB
A DynamoDB table with encryption, point-in-time recovery and deletion protection on. On-demand billing, with attribute definitions derived for you.
Infrastructure
import { App } from '@anvil-cloud/sdk';
import * as anvil from '@anvil-cloud/sdk';
export default new App({
run(ctx) {
const table = new anvil.aws.DynamoDB('orders', {
hashKey: { name: 'pk', type: 'S' },
rangeKey: { name: 'sk', type: 'S' },
ttlAttribute: 'expiresAt',
});
const fn = new anvil.aws.Lambda('processor', {
runtime: 'nodejs24.x',
handler: 'functions/processor/index.handler',
environment: {
TABLE_NAME: table.tableName,
},
});
// Read and write items in this table only (not its indexes).
table.grantReadWrite(fn);
ctx.export('tableName', table.tableName);
},
});import anvil_cloud as anvil
def infra(ctx: anvil.Context):
table = anvil.aws.DynamoDB("orders",
hash_key={"name": "pk", "type": "S"},
range_key={"name": "sk", "type": "S"},
ttl_attribute="expiresAt",
)
fn = anvil.aws.Lambda("processor",
runtime="nodejs24.x",
handler="functions/processor/index.handler",
environment={"TABLE_NAME": table.table_name},
)
# Read and write items in this table only (not its indexes).
table.grant_read_write(fn)
ctx.export("table_name", table.table_name)
anvil.App(run=infra)package main
import (
"github.com/anvil-cloud/anvil/sdk/go/anvil"
anvilaws "github.com/anvil-cloud/anvil/sdk/go/anvil/aws"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
anvil.Run(anvil.AppConfig{
Run: func(ctx *anvil.Context) error {
table, err := anvilaws.NewDynamoDB(ctx.PulumiCtx(), "orders", &anvilaws.DynamoDBArgs{
HashKey: &anvilaws.DynamoDBKeyAttributeArgs{Name: pulumi.String("pk"), Type: anvilaws.DynamoDBAttributeTypeS},
RangeKey: &anvilaws.DynamoDBKeyAttributeArgs{Name: pulumi.String("sk"), Type: anvilaws.DynamoDBAttributeTypeS},
TtlAttribute: pulumi.String("expiresAt"),
})
if err != nil {
return err
}
_, err = anvilaws.NewLambda(ctx.PulumiCtx(), "processor", &anvilaws.LambdaArgs{
Runtime: anvilaws.LambdaRuntime_Nodejs24_x,
Handler: pulumi.String("functions/processor/index.handler"),
Environment: pulumi.StringMap{"TABLE_NAME": table.TableName},
// Go has no grant methods yet: the same actions as grantReadWrite(fn).
Permissions: anvilaws.LambdaPermissionArray{
anvilaws.LambdaPermissionArgs{
Actions: pulumi.ToStringArray([]string{
"dynamodb:GetItem", "dynamodb:BatchGetItem", "dynamodb:Query", "dynamodb:Scan",
"dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:BatchWriteItem",
}),
Resource: table.TableArn,
},
},
})
if err != nil {
return err
}
ctx.Export("tableName", table.TableName)
return nil
},
})
}Runtime code
Works from any compute granted access (Lambda, ECS, EC2…), using each language's AWS SDK. Set TABLE_NAME in its environment.
// orders.ts
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, PutCommand, GetCommand } from '@aws-sdk/lib-dynamodb';
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
const table = process.env.TABLE_NAME!;
export async function saveOrder(orderId: string, status: string): Promise<void> {
await ddb.send(new PutCommand({
TableName: table,
Item: { pk: `ORDER#${orderId}`, sk: 'METADATA', status },
}));
}
export async function getOrder(orderId: string) {
const { Item } = await ddb.send(new GetCommand({
TableName: table,
Key: { pk: `ORDER#${orderId}`, sk: 'METADATA' },
}));
return Item;
}# orders.py
import os
import boto3
table = boto3.resource("dynamodb").Table(os.environ["TABLE_NAME"])
def save_order(order_id: str, status: str) -> None:
table.put_item(Item={"pk": f"ORDER#{order_id}", "sk": "METADATA", "status": status})
def get_order(order_id: str):
return table.get_item(Key={"pk": f"ORDER#{order_id}", "sk": "METADATA"}).get("Item")// orders.go
package orders
import (
"context"
"os"
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
)
var table = os.Getenv("TABLE_NAME")
type Order struct {
PK string `dynamodbav:"pk"`
SK string `dynamodbav:"sk"`
Status string `dynamodbav:"status"`
}
// SaveOrder writes an order. Create the client once, with
// dynamodb.NewFromConfig(cfg) after config.LoadDefaultConfig(ctx).
func SaveOrder(ctx context.Context, client *dynamodb.Client, orderID, status string) error {
item, err := attributevalue.MarshalMap(Order{PK: "ORDER#" + orderID, SK: "METADATA", Status: status})
if err != nil {
return err
}
_, err = client.PutItem(ctx, &dynamodb.PutItemInput{TableName: &table, Item: item})
return err
}
// GetOrder reads an order, or returns nil if there is none.
func GetOrder(ctx context.Context, client *dynamodb.Client, orderID string) (*Order, error) {
key, err := attributevalue.MarshalMap(map[string]string{"pk": "ORDER#" + orderID, "sk": "METADATA"})
if err != nil {
return nil, err
}
out, err := client.GetItem(ctx, &dynamodb.GetItemInput{TableName: &table, Key: key})
if err != nil || out.Item == nil {
return nil, err
}
var o Order
return &o, attributevalue.UnmarshalMap(out.Item, &o)
}