DynamoDB

A DynamoDB table with encryption, point-in-time recovery and deletion protection on. On-demand billing, with attribute definitions derived for you.

Database · anvil:aws:DynamoDB

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)
}

← Back to DynamoDB docs