ProvenPin Install

AWS SDK v3 DynamoDB document client and SQS events, tested with Vitest matchers: working versions (tested 2026-10-15)

A single-table DynamoDB order service on @aws-sdk/lib-dynamodb with conditional puts, begins_with queries and optimistic locking, publishing SQS events, tested offline with aws-sdk-client-mock and aws-sdk-client-mock-vitest matchers.

setup_failed0 passed · 1 failed2026-10-15 14:10 UTC
Status
setup_failed
Last passed
2026-10-15 14:10 UTC
Tests
0 / 1
Language
TypeScript
Runtime
node v22.23.3
pinned versions7
@aws-sdk/client-dynamodb@3.1152.0@aws-sdk/lib-dynamodb@3.1152.0@aws-sdk/client-sqs@3.1152.0aws-sdk-client-mock@4.1.0aws-sdk-client-mock-vitest@8.0.0vitest@5.0.4@types/node@26.6.5
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 38755782594

The code

Exactly the files that passed. Your agent can pull them with get_recipe("aws-sdk-v3-dynamodb-sqs-vitest") (install), or see them on GitHub.

package.json
{
  "name": "aws-sdk-v3-dynamodb-sqs-vitest",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "vitest run --reporter=default --reporter=json --outputFile=test-report.json"
  },
  "dependencies": {
    "@aws-sdk/client-dynamodb": "3.1152.0",
    "@aws-sdk/lib-dynamodb": "3.1152.0",
    "@aws-sdk/client-sqs": "3.1152.0"
  },
  "devDependencies": {
    "aws-sdk-client-mock": "4.1.0",
    "aws-sdk-client-mock-vitest": "8.0.0",
    "vitest": "5.0.4",
    "@types/node": "26.6.5"
  }
}
src/orders.ts
import { DynamoDBClient, ConditionalCheckFailedException } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand, GetCommand, UpdateCommand, QueryCommand } from "@aws-sdk/lib-dynamodb";
import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";

export type Order = { pk: string; sk: string; orderId: string; customerId: string; status: "pending" | "paid" | "shipped"; totalCents: number; version: number };

const creds = { accessKeyId: "AKIDEXAMPLE", secretAccessKey: "secret" };
export const makeClients = () => ({
  // The document client marshalls plain JS objects, so no { S: "..." } attribute maps in app code.
  ddb: DynamoDBDocumentClient.from(new DynamoDBClient({ region: "us-east-1", credentials: creds }), { marshallOptions: { removeUndefinedValues: true } }),
  sqs: new SQSClient({ region: "us-east-1", credentials: creds }),
});

export class OrderService {
  constructor(private c: ReturnType<typeof makeClients>, private table = "app", private queueUrl = "https://sqs.us-east-1.amazonaws.com/123456789012/order-events") {}

  /** Single-table design: customer partition, order sort key. Refuses to overwrite an existing order. */
  async create(customerId: string, orderId: string, totalCents: number): Promise<Order> {
    const order: Order = { pk: `CUSTOMER#${customerId}`, sk: `ORDER#${orderId}`, orderId, customerId, status: "pending", totalCents, version: 1 };
    await this.c.ddb.send(new PutCommand({ TableName: this.table, Item: order, ConditionExpression: "attribute_not_exists(pk)" }));
    await this.publish("order.created", order);
    return order;
  }

  get(customerId: string, orderId: string) {
    return this.c.ddb.send(new GetCommand({ TableName: this.table, Key: { pk: `CUSTOMER#${customerId}`, sk: `ORDER#${orderId}` } })).then((r) => (r.Item as Order | undefined) ?? null);
  }

  async listForCustomer(customerId: string) {
    const r = await this.c.ddb.send(new QueryCommand({
      TableName: this.table,
      KeyConditionExpression: "pk = :pk AND begins_with(sk, :prefix)",
      ExpressionAttributeValues: { ":pk": `CUSTOMER#${customerId}`, ":prefix": "ORDER#" },
      ScanIndexForward: false,
    }));
    return (r.Items ?? []) as Order[];
  }

  /** Optimistic locking: the update only applies if the version we read is still current. */
  async setStatus(order: Order, status: Order["status"]) {
    try {
      const r = await this.c.ddb.send(new UpdateCommand({
        TableName: this.table,
        Key: { pk: order.pk, sk: order.sk },
        UpdateExpression: "SET #s = :s, version = version + :one",
        ConditionExpression: "version = :v",
        ExpressionAttributeNames: { "#s": "status" },
        ExpressionAttributeValues: { ":s": status, ":v": order.version, ":one": 1 },
        ReturnValues: "ALL_NEW",
      }));
      const updated = r.Attributes as Order;
      await this.publish(`order.${status}`, updated);
      return updated;
    } catch (e) {
      if (e instanceof ConditionalCheckFailedException) return null; // someone else changed it first
      throw e;
    }
  }

  private publish(type: string, order: Order) {
    return this.c.sqs.send(new SendMessageCommand({
      QueueUrl: this.queueUrl,
      MessageBody: JSON.stringify({ type, orderId: order.orderId, status: order.status }),
      MessageAttributes: { type: { DataType: "String", StringValue: type } },
    }));
  }
}
test/orders.test.ts
import { describe, it, expect, beforeEach } from "vitest";
import { mockClient } from "aws-sdk-client-mock";
import "aws-sdk-client-mock-vitest/extend";
import { DynamoDBDocumentClient, PutCommand, GetCommand, UpdateCommand, QueryCommand } from "@aws-sdk/lib-dynamodb";
import { ConditionalCheckFailedException } from "@aws-sdk/client-dynamodb";
import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";
import { OrderService, makeClients, type Order } from "../src/orders.ts";

// Mock the document client itself, so assertions see plain JS inputs rather than marshalled ones.
const ddbMock = mockClient(DynamoDBDocumentClient);
const sqsMock = mockClient(SQSClient);
const svc = () => new OrderService(makeClients());

beforeEach(() => { ddbMock.reset(); sqsMock.reset(); sqsMock.on(SendMessageCommand).resolves({ MessageId: "m-1" }); });

const order: Order = { pk: "CUSTOMER#c1", sk: "ORDER#o1", orderId: "o1", customerId: "c1", status: "pending", totalCents: 4200, version: 1 };

describe("OrderService", () => {
  it("creates with a conditional put and publishes an event", async () => {
    ddbMock.on(PutCommand).resolves({});
    await expect(svc().create("c1", "o1", 4200)).resolves.toEqual(order);
    expect(ddbMock).toHaveReceivedCommandWith(PutCommand, { TableName: "app", Item: order, ConditionExpression: "attribute_not_exists(pk)" });
    expect(sqsMock).toHaveReceivedCommandTimes(SendMessageCommand, 1);
    const body = JSON.parse(sqsMock.commandCalls(SendMessageCommand)[0].args[0].input.MessageBody!);
    expect(body).toEqual({ type: "order.created", orderId: "o1", status: "pending" });
  });

  it("does not publish when the put fails", async () => {
    ddbMock.on(PutCommand).rejects(new ConditionalCheckFailedException({ message: "exists", $metadata: {} }));
    await expect(svc().create("c1", "o1", 1)).rejects.toBeInstanceOf(ConditionalCheckFailedException);
    expect(sqsMock).not.toHaveReceivedCommand(SendMessageCommand);
  });

  it("gets by composite key and returns null when missing", async () => {
    // register the catch-all first: a later, more specific input match takes precedence
    ddbMock.on(GetCommand).resolves({});
    ddbMock.on(GetCommand, { Key: { pk: "CUSTOMER#c1", sk: "ORDER#o1" } }).resolves({ Item: order });
    expect(await svc().get("c1", "o1")).toEqual(order);
    expect(await svc().get("c1", "nope")).toBeNull();
  });

  it("queries a customer's orders newest first", async () => {
    ddbMock.on(QueryCommand).resolves({ Items: [order] });
    expect(await svc().listForCustomer("c1")).toHaveLength(1);
    expect(ddbMock).toHaveReceivedCommandWith(QueryCommand, {
      KeyConditionExpression: "pk = :pk AND begins_with(sk, :prefix)",
      ExpressionAttributeValues: { ":pk": "CUSTOMER#c1", ":prefix": "ORDER#" },
      ScanIndexForward: false,
    });
  });

  it("updates status with optimistic locking", async () => {
    ddbMock.on(UpdateCommand).resolves({ Attributes: { ...order, status: "paid", version: 2 } });
    const updated = await svc().setStatus(order, "paid");
    expect(updated).toMatchObject({ status: "paid", version: 2 });
    expect(ddbMock).toHaveReceivedCommandWith(UpdateCommand, { ConditionExpression: "version = :v", ExpressionAttributeValues: { ":s": "paid", ":v": 1, ":one": 1 } });
    expect(sqsMock).toHaveReceivedCommandWith(SendMessageCommand, { MessageAttributes: { type: { DataType: "String", StringValue: "order.paid" } } });
  });

  it("returns null on a version conflict and publishes nothing", async () => {
    ddbMock.on(UpdateCommand).rejects(new ConditionalCheckFailedException({ message: "stale", $metadata: {} }));
    expect(await svc().setStatus(order, "shipped")).toBeNull();
    expect(sqsMock).not.toHaveReceivedCommand(SendMessageCommand);
  });
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.