Reputation: 187
I am using AWS Amplify Hosting (Gen 2) and have created a REST API backed by Lambda. The Lambda function is supposed to send an email (which works fine) and save the submitted data to a DynamoDB table. However, when trying to save data, I get the following error:
ResourceNotFoundException: Requested resource not found
I have verified that the table name is correct and tested using the specific table ARN. Additionally, I tried using a wildcard for table names to ensure broader access permissions.
This is my API Stack.
// create a new API stack
const apiStack = backend.createStack("api-stack");
// create a new REST API
const myRestApi = new RestApi(apiStack, "RestApi", {
restApiName: "myRestApi",
deploy: true,
deployOptions: {
stageName: "dev",
},
defaultCorsPreflightOptions: {
allowOrigins: Cors.ALL_ORIGINS, // Restrict this to domains you trust
allowMethods: Cors.ALL_METHODS, // Specify only the methods you need to allow
allowHeaders: Cors.DEFAULT_HEADERS, // Specify only the headers you need to allow
},
});
backend.ApiContactFormFunction.resources.lambda.addToRolePolicy(
new PolicyStatement({
actions: ['ses:SendEmail', 'ses:SendRawEmail'],
resources: ['*']
}),
)
backend.ApiContactFormFunction.resources.lambda.addToRolePolicy(
new PolicyStatement({
effect: Effect.ALLOW,
actions: [
'dynamodb:PutItem',
],
resources: [
"*"
]
})
)
// create a new Lambda integration
const lambdaIntegration = new LambdaIntegration(
backend.ApiContactFormFunction.resources.lambda
);
// create a new resource path with IAM authorization
const itemsPath = myRestApi.root.addResource("contact-form", {
defaultMethodOptions: {
authorizationType: AuthorizationType.NONE,
},
});
// add methods you would like to create to the resource path
itemsPath.addMethod("GET", lambdaIntegration);
itemsPath.addMethod("POST", lambdaIntegration);
itemsPath.addMethod("DELETE", lambdaIntegration);
itemsPath.addMethod("PUT", lambdaIntegration);
// add a proxy resource path to the API
itemsPath.addProxy({
anyMethod: true,
defaultIntegration: lambdaIntegration,
});
// create a new IAM policy to allow Invoke access to the API
const apiRestPolicy = new Policy(apiStack, "RestApiPolicy", {
statements: [
new PolicyStatement({
actions: ["execute-api:Invoke"],
resources: [
`${myRestApi.arnForExecuteApi("*", "/contact-form", "dev")}`,
],
}),
],
});
// attach the policy to the authenticated and unauthenticated IAM roles
backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(
apiRestPolicy
);
backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy(
apiRestPolicy
);
// add outputs to the configuration file
backend.addOutput({
custom: {
API: {
[myRestApi.restApiName]: {
endpoint: myRestApi.url,
region: Stack.of(myRestApi).region,
apiName: myRestApi.restApiName,
},
},
},
});
This is my lambda function:
import type { APIGatewayProxyHandler, APIGatewayProxyHandlerV2 } from "aws-lambda";
import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
const ddbClient = new DynamoDBClient({ region: 'eu-west-1' });
const sesClient = new SESClient({ region: 'af-south-1' });
export const handler: APIGatewayProxyHandler = async (event) => {
console.log("event", event);
const body = JSON.parse(event.body || '{}');
const { fullName, email, phoneNumber, message } = body;
const recipient = '[email protected]';
const subject = `Contact Form - ${fullName}`;
const htmlTemplate = `Email Body`;
const command = new SendEmailCommand({
Source: '[email protected]',
Destination: {
ToAddresses: [recipient]
},
Message: {
Body: {
Html: { Data: htmlTemplate }
},
Subject: { Data: subject }
}
});
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*", // Restrict this to domains you trust
"Access-Control-Allow-Headers": "*", // Specify only the headers you need to allow
},
body: '',
};
try {
const result = await sesClient.send(command);
console.log(`Email sent to ${recipient}: ${result.MessageId}`);
const tbCommand = new PutItemCommand({
TableName: 'Table',
Item: {
email: { S: 'Test email' },
message: { S: 'Test message' },
phone: { S: 'Test phone' },
fullName: { S: 'Fullname' }
},
});
await ddbClient.send(tbCommand);
response.body = JSON.stringify(`Email have been send to ${fullName} - ${email}}!`)
} catch (error) {
response.statusCode = 500;
response.body = JSON.stringify(`There was an error sending the email: ${error}`)
}
return response;
};
Any help would be appreciated.
Upvotes: 0
Views: 248
Reputation: 19883
Make sure you use the correct table name in the following form:
The table name is in the form [Name]-[AppSyncApiId]-[env]
Then you can resolve it similar to this, depending on how you build your infra:
"Fn::ImportValue": {
"Fn::Sub": "${AppSyncApiId}:GetAtt:CoursesTable:Name"
}
Upvotes: 1