0

In an AWS Amplify backed application, I can define two data models Inventory and Order. Then Amplify generates a GraphQL API and underlying DynamoDB tables for these models.

Then I can create a function to handle new orders. What it needs to do is check if there's enough inventory to complete the order, and if there is, update both inventory and order.

In this case, I would like to have the function read the respective table names from environment variables. That way I can create multiple Amplify stacks and the different functions will be configured to use the tables that belong to their stack.

I create the function like this

// amplify/functions/resource.ts
export const dynamoDbDataHandlerFunction = defineFunction(
    (scope) =>
        new Function(scope, "dynamoDB-data-handler-function", {
            handler: Handler.FROM_IMAGE,
            runtime: Runtime.FROM_IMAGE,
            timeout: Duration.minutes(2), //  default is 3 seconds
            code: Code.fromEcrImage(
                Repository.fromRepositoryArn(...),
                {tagOrDigest: "v1.2.3"}
            ),
            memorySize: 256,
            environment: {
                // environment variables
            }
        }),
    {
        resourceGroupName: "data"
    }
);

However, at this point, and in this file (amplify/functions/resource.ts) I don't see a way to pull in the backend construct (defined in amplify/backend.ts) where I could read the DynamoDB table names.

// amplify/backend.ts
const backend = defineBackend({
  auth,
  data,
  dynamoDbDataHandlerFunction,
});

const inventoryTable = backend.data.resources.tables["Inventory"];
const orderTable = backend.data.resources.tables["Order"];

// at this point I can access inventoryTable.tableName or orderTable.tableName

Is it possible to wire the different resources together?

1 Answer 1

0

It seems that the way to wire the resources together when there are no higher-level APIs for it, is to use the lower-level CDK constructs, in this case the L1 CloudFormation resources.

In the example I gave in the question, that would like like this.

// amplify/backend.ts

// ...

const inventoryTable = backend.data.resources.tables["Inventory"];
const orderTable = backend.data.resources.tables["Order"];

// Wire environment variables for data handler function
const { cfnFunction } = backend.dynamoDbDataHandlerFunction.resources.cfnResources;
cfnFunction.environment = {
    variables: {
        DYNAMODB_TABLE_INVENTORY_NAME: inventoryTable.tableName,
    }
}

I wasn't able to wire the orders table as well because that would introduce a cyclic dependency:

  • Order table -> depends on function -> function depends on Order table

I'm looking at two ways to deal with this:

  1. Instead of configuring the tables names, extract the stack identifiers (eg. ha6yrzzuxneh3avsibzxqufxsy-NONE) from the inventory table name (eg. Inventory-ha6yrzzuxneh3avsibzxqufxsy-NONE). Then configure only the stack identifier and hardcode the table names.

  2. Configure the inventory table name (as I did above) and then extract the order table name from the DynamoDB stream records that the function receives. This option makes dependency injection harder since the table name isn't known at startup.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.