見出し画像

AWS Amplify DynamoDBのトランザクション

前回の記事でLambdaリゾルバーでDynamoDBのアイテムを一括で変更する方法を試しました。

モデルのリレーションで使われる中間テーブルなどがある場合は、BatchWriteItemよりもトランザクションが効くTransactWriteItemsの方が良さそうだったので、そちらを試してみました。
毎度書き方がわからずエラーと格闘になりますが、以下の様なコードでうまくいきました。

/* Amplify Params - DO NOT EDIT
...(略)
	ENV
	REGION
Amplify Params - DO NOT EDIT */

import crypto from "crypto";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, BatchWriteCommand, TransactWriteCommand } from "@aws-sdk/lib-dynamodb";

/**
 * Create project.
 * 
 * @type {import('@types/aws-lambda').APIGatewayProxyHandler}
 */
export const handler = async (event, context, callback) => {
    console.log(`EVENT: ${JSON.stringify(event)}`);

    // Authorization
    (認証処理、略)

    const projectName = event.arguments.name;
    if (!projectName || projectName === "") {
        return callback("name is required.");
    }

    const client = new DynamoDBClient({});
    const docClient = DynamoDBDocumentClient.from(client);

    const projectId = crypto.randomUUID();
    const date = new Date();
    
    const project = {
        id: projectId,
        name: projectName,
        owner: userId + ":" + userId,
        createdAt: date.toISOString(),
        updatedAt: date.toISOString(),
        __typename: 'Project'
    };
    
    const params = {
        TransactItems: [
            {
                Put: {
                    TableName: process.env["API_KANBANGANTT_PROJECTTABLE_NAME"],
                    Item: project
                }
            },
            {
                Put: {
                    TableName: process.env["API_KANBANGANTT_PROJECTUSERTABLE_NAME"],
                    Item: {
                        id: crypto.randomUUID(),
                        projectId: projectId,
                        userId: userId,
                        owner: userId + "::" + userId,
                        createdAt: date.toISOString(),
                        updatedAt: date.toISOString(),
                        __typename: 'ProjectUser'
                    }
                }
            },
            ...(その他のテーブルのItem 略)
        ]
    };

    const res = await docClient.send(new TransactWriteCommand(params));
    
    const resProject = project;
    resProject.owner = userId;
    return callback(null, {
        data: resProject
    });
};

小ネタとして、新規アイテムに登録する値をどうするかは以下の通り。

id
idはNode.js標準のcrypto.randomUUID()で取得で良さそう。

owner
ownerフィールドですがschema.grapqlのauthでownerを使った場合、DynamoDBには単純にCognitoのユーザーIDではなく「{ユーザーID}:{ユーザーID}」という「::」区切りのフォーマットが登録されます。しかし、クライアントに返される値は単純なユーザーIDになります。よってLambdaでもその様に実装しています。
何故この様なフォーマットになっているかはちゃんと調べていないので謎ですが恐らく他の認証モードかCognitoの色々な設定に対応するためかもしれません。

createdAt、updatedAt
Date.toISOString()。(タイムゾーンの影響など未検証。)

だんだんLambdaリゾルバーで必要なスニペットやノウハウが溜まってきて開発スピードが上がってきたので楽しくなってきました。
それではまた次の記事で。

もしこの記事があなたのお役に立てたなら幸いです。 よろしければサポートをお願いします。今後の制作資金にさせていただきます!