Open antennix opened 4 months ago
Hi @antennix, you can grant a function access to the Data API by following these docs: https://docs.amplify.aws/react/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/
We are also actively investigating making the experience of configuring the data client in the context of a lambda function a better experience, so any feedback you have there is welcome!
@edwardfoyle Thank you. I understand the current approach to Data (AppSync). I'm hoping that the process of generating GraphQL code will become simpler.
On the other hand, is there an approach for when we want to access the data source (Dynamo) directly?
For example, executing a daily batch via Function (I believe the scheduling feature will be implemented soon: https://github.com/aws-amplify/amplify-backend/issues/1491), and performing specific processing on DynamoDB records.
We could update records through AppSync, but I don't think we can do so without users who are executing Subscriptions noticing. I want to update silently.
Hi, @antennix , I bumped into the same issue a couple of days ago when trying to pass the dynamoDB table name to my lambda hander as an env variable, and I did exact the same thing as you mentioned in the description section, which caused a circular dependencies
issue.
Then, I joined in a discussion in another similar open issue #2554, where we believe this could be some sort of bug. But fortunately, other great devs came up with some workarounds, which I already tested in my case and they worked just fine. And I hope they could work for you too. Good luck!
@MyNameIsTakenOMG Thank you. I think the Lambda custom resource that indirectly retrieves the resource name is a very good idea.
Marking as feature request to provide Data parameters such as table name and arn as environment variables.
I have solved it by adding this to my backend.ts where Event is one of the resources I want access to
// Get the DynamoDB table name
const tableName = backend.data.resources.tables["Event"].tableName;
new cdk.CfnOutput(eventBridgeStack, "EventTableName", {
value: tableName,
description: "The imported name of the Event DynamoDB table",
exportName: "EventTableName",
});
In the stack where I create the lambda function I use:
// Define Lambda function
this.lambdaFunction = new lambda.Function(this, "CheckEventsNextWeek", {
runtime: lambda.Runtime.NODEJS_20_X,
code: lambda.Code.fromAsset(
"amplify/custom/services/checkEventsNextWeek/src"
),
handler: "index.handler",
environment: {
DB_EVENT_TABLE_NAME: cdk.Fn.importValue("EventTableName"),
},
});
Inside the lambda you can the use: process.env.DB_EVENT_TABLE_NAME
This works like charm
I understand the mechanism of preventing circular references by creating Lambda without using DefineFunction. Thank you.
On the other hand, I'd like to create Lambda using defineFunction to make integration with the Amplify system easier.
my workaround is to:
be.addOutput({
custom: {
webhookUrl: functionUrl.url,
stateMachineArn: notificationsStack.stateMachine.stateMachineArn,
},
});
but now we have a chicken and egg issue because the outputs are artefacts of the build...
version: 1
backend:
phases:
preBuild:
commands:
- npx ampx generate outputs --branch $AWS_BRANCH --app-id $AWS_APP_ID
build:
commands:
- npm ci --cache .npm --prefer-offline
- npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID
some things to note...
as we do text refs we won't have any cloudformation dependencies...
idk... hope for an official solution.
I am now thinking of using @rpostulart solution now... :)
hoping for an official stand :)
@rpostulart's solution works, but I encountered a significant issue with that approach after pushing to my main branch and attempting to start up my sandbox again:
Error:
Export with name EXPORT is already exported by stack ...
I resolved it by using a different approach: casting the imported function resource in backend.ts
as aws_lambda.Function
to enable the addEnvironment
method, as suggested by Jesse Pangburn in this thread.
Here’s a code snippet:
const customResourceStack = backend.createStack("ProcessingStack");
export const embeddingQueue = new sqs.Queue(
customResourceStack,
"EmbeddingQueue"
);
// Add IAM and environment variables to the function
const createEmbeddingsFunction =
backend.createEmbeddingsForLaws.resources.lambda as aws_lambda.Function; // CAST HERE!
embeddingQueue.grantSendMessages(createEmbeddingsFunction); // IAM
createEmbeddingsFunction.addEnvironment("SQS_QUEUE_URL", embeddingQueue.queueUrl); // ENV
Note: This may cause circular dependency issues, as explained here, but this applies primarily to DynamoDB, not SQS/SNS.
Also, when accessing the environment variable in the Lambda function, do not import the env
from the .amplify/generated/env
folder, as it won’t work. Instead, use process.env
; the variable should be available there. Although it's not type-safe, it works.
@pridapablo I got into a similar error but in my scenario I was trying to deploy 2 branches.
This is caused by the fact that cloudformation exports space si scoped to the account level
For me, the current workaround is to define a secret at the app level and put it in there manually... and before init a local sandbox, ... and after I deploy a new branch. This gives you the option to have diff values per env.
My mentioned solution with importing outputs is also bad, as it only works with a previous deployment. as in: we can't do a new branch from scratch...
Environment information
Description
Is it possible for a Function to receive the Endpoint of Data, or the name and ARN of Datasources like DynamoDB?
For example, with Storage, you can use the "access" property in the defineStorage function to grant read and write permissions to a Function. https://docs.amplify.aws/react/build-a-backend/functions/grant-access-to-other-resources/
However, a similar approach cannot be used with defineData as it's not possible to specify properties.
It is possible to directly set values for an already created function in backend.ts using CDK, but if you're also using defineFunction for resolvers against Data, I think an error would occur due to circular reference, as the reference order would be resolverFunction → Data → thisFunction.
Is there a good solution?