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.
passed6 passed · 0 failed2026-10-15 14:10 UTC- Status
- passed
- Last passed
- 2026-10-15 14:10 UTC
- Tests
- 6 / 6
- Language
- TypeScript
- Runtime
- node v22.23.3
npm install --no-audit --no-fund --ignore-scripts=false npm test
node v22.23.3 linux 6.17.0-1022-azure x64 runner: github-actions 38748988915
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.
{
"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"
}
}
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 } },
}));
}
}
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.
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.
Related stacks
Astro 7 component rendering tests with the Container API (Vitest)
Render Astro components to HTML in Vitest with the Container API and getViteConfig, checking props, slots and conditional markup.
AWS SDK v3 S3 storage helpers tested with aws-sdk-client-mock
S3 helpers on @aws-sdk/client-s3 for JSON put/get with NoSuchKey handling, paginated listing, batched DeleteObjects and locally signed presigned URLs, tested offline with aws-sdk-client-mock and sdkStreamMixin bodies.
Effect 4 Schema classes, typed errors and retries, tested with @effect/vitest
Effect 4 Schema.Class models with checks, brands, FiniteFromString and fromJsonString for decode and encode, TaggedError failures, and Effect.retry with exponential backoff verified on the TestClock using @effect/vitest.
Hono + Drizzle ORM + SQLite REST API (Vitest)
CRUD REST API in Hono backed by Drizzle ORM on better-sqlite3, with Zod validation, tested with Vitest and app.request().
All @aws-sdk/client-dynamodb recipesAll @aws-sdk/lib-dynamodb recipesAll @aws-sdk/client-sqs recipesAll aws-sdk-client-mock recipesAll vitest recipes