I'm new to Lambda Computing and AWS. I'm trying to set up a REST API Service with Lambda. I have three different functions defined in my serverless.yml file from my handler something like this:
functions:
users:
handler: handler.users
events:
- http:
path: users
method: get
cors: true
integration: lambda
stats:
handler: handler.Stats
events:
- http:
path: users/{id}/stats
method: get
cors: true
integration: lambda
contribution:
handler: handler.contribution
events:
- http:
path: patientset/{pid}/contribution
method: get
cors: true
integration: lambda
When I deploy the same, it throws me an error:
Serverless Error ---------------------------------------
An error occurred: ApiGatewayResourcePatientsetPidVar - A sibling ({id}) of this resource already has a variable path part -- only one is allowed (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: f4047858-b52d-4721-b0a2-069a59254e6b; Proxy: null).
So to test it, I change the path for the function contribution from patientset/{pid}/contribution to patientset/{id}/contribution, the deployment is successful.
But this is what I don't require. I need to define the proper name convention for each function to help recognize what kind of id a function is expecting as it's path parameter.
I also tried resolving the same with some suggestions around as found here, but this also didn't help. If anyone can help provide any solution for the same & this explains this weird behavior from serverless, highly appreciated.
Thanks in advance.
Check if path is not repeated for any of handlers
Comment the section causing issue and deploy once.
Then uncomment then section, redeploy.
Its a known bug in serverless with AWS
I faced same issue got resolved with this.
Seems to be a long standing AWS issue where changing the endpoint variable causes an issue
https://github.com/serverless/serverless/issues/3785
Related
In my SAM templates, my team has defined an API that is mostly to our liking. I would like to debug this API locally, but it isn't set explicitly as an Event under our Function. So sam local start-api fails with the error
Error: Template does not have any APIs connected to Lambda functions
How can I convince SAM that the API we have defined is the event meant to invoke this Lambda? What should I do to test this locally?
edit - to clarify, the current template structure looks something like
Lambda:
Type: AWS::Serverless::Function
Properties:
...
LambdaRole:
....
MAILAPI:
Type: AWS::Serverless::Api
Properties:
...
Not sure if this implements all the gateway params we defined so I wont mark this as resolved yet, but this is a promising start!
This allowed me to start the API as expected locally
Events:
Api:
Type: Api
Properties:
Path: /
Method: post
RestApiId:
Ref: MAILAPI
With (of course) our API resource defined under the MAILAPI label (edited question to show this)
As part of a AWS SAM template, I have a function with an HttpPost event trigger. Because I'm using the AWS SAM transform, I am not explicitly declaring the API Gateway that gets created to route this http post to trigger the function. Given that, is there any way to reference the generated URL endpoint, such as in a stack output or describe-stack-resources, so that I can programatically get the invocation URL for the function? I know I can get the endpoint by navigating to the stack in the console, finding the ApiGateway resource, and clicking around randomly until one of the pages shows it. But I'd like a method that my application code can reproduce.
Shortened template for reference:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
...
SendJobUpdateFunction:
Type: AWS::Serverless::Function
Properties:
...
Runtime: nodejs10.x
Events:
HttpPost:
Type: Api
Properties:
Path: '/jobs'
Method: post
...
I'm currently deploying using the sam CLI, which has I think a very similar syntax to aws cloudformtion.
According to the documentation and this previous question, you can get it with:
!Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/${Stage}"
Where ${Stage} is your own parameter containing the deployed stage.
I wanted to make a lambda available at dev-api.example.com/auth/*.
The lambda will act like an auth service. So it will have urls like
dev-api.example.com/auth/register
dev-api.example.com/auth/login
and more ...
Like wise more lambdas will be hooked to single ApiGateway.
With that design decision, I wrote following serverless.yml file.
// serverless.yml
...
custom:
customDomain:
domainName: dev-api.example.com
stage: prod
basePath: ''
...
functions:
auth:
handler: src/index.handler
events:
- http:
method: ANY
path: /{auth+}
It does not seem to work. Whenever I visit dev-api.example.com/auth/register it returns Not Found error.
AWS API Gateway only accepts {proxy+} syntax (Link), then I think serverless fw just support {proxy+} and {any+}.
If you want to just create a function to handle 2 api endpoint, in this case, the endpoints are
POST /auth/register (I think so)
POST /auth/login
Then you have setting in serverless.yml like
...
functions:
auth:
handler: src/index.handler
events:
- http:
method: ANY
path: auth/{any+} # this matches any path, the token 'any' doesn't mean anything special
...
Thanks #hoangdv , your suggestion almost fixed the problem.
The issue was with path. It should have been path: auth/{proxy+} instead of path: /{auth+}
functions:
auth:
handler: src/index.handler
events:
- http:
method: ANY
path: auth/{proxy+}
Lambda now supports adding SNS topics (among other things) as destinations. This can be set up via the UI.
But I can't get it to work. I have a simple Lambda that returns a JSON that I want it to push to an SNS topic. I open the Lambda's destination and in the destination pasted the topic. This resulted in "Invalid input".
To get the destination to recognize the topic ARN, I first gave the Lambda SNS Full Access and then also added my Lambda role to the SNS topic Access policy.
"Resource": [topic arn],
"Condition": {
"StringEquals": {
"AWS:SourceOwner": [topic owner],
"Role": [Lambda role arn] <---single added line
}
}
No more "Invalid input"!
Unfortunately, when I return to Lambda and go through the Destination flow again, I now get
The provided destination config
DestinationConfig(onSuccess=OnSuccess(destination=[topic arn]),
onFailure=null) is invalid.
But one can only define Success or Failure (radio buttons), not both. So presumably I mucked up permissions somewhere and the Lambda actually can't publish still.
What permissions do I need to grant the Lambda role, and how do I need to update the Topic access to make this work?
Edit: VPCs
I failed to mention my Lambda sits on a VPC. Because I'm calling an external database and need a whitelisted IP, I'm using a VPC/NAT setup to keep a stable IP.
I'm not sure if this effects my situation, but from my rudimentary understanding of VPCs, I'm guessing this limits what the Lambda can directly interact with.
Taking a look around, the Asynchronous Invocation docs have a section on Lambda destinations. That section seems to indicate that the only permission you need is to give is sns:Publish to your Lambda. Can you confirm that it does in fact receive publish permissions?
If that's not the issue, I would try taking their Cloudformation template (quoted below - from your link above) and comparing it to the Cloudformation being generated in your account when you do setup via the console.
Resources:
EventInvokeConfig:
Type: AWS::Lambda::EventInvokeConfig
Properties:
FunctionName: “YourLambdaFunctionWithEventInvokeConfig”
Qualifier: "$LATEST"
MaximumEventAgeInSeconds: 600
MaximumRetryAttempts: 0
DestinationConfig:
OnSuccess:
Destination: “arn:aws:sns:us-east-1:123456789012:YourSNSTopicOnSuccess”
OnFailure:
Destination: “arn:aws:lambda:us-east-1:123456789012:function:YourLambdaFunctionOnFailure”
Note that from the docs, it looks like you can define OnSuccess and OnFailure sequentially in the console, by going through the process twice. I wouldn't quite put it past them to have e.g. an implicit requirement that both OnSuccess and OnFailure be provided.
Maybe different to your case, but I found out the reason I was getting this error was simply because the SNS was in a different region to the lambda. Ensuring it was the same region worked as per below example. Wish the error was clearer here!
service: errorhandlingdemo
frameworkVersion: '2'
configValidationMode: error
plugins:
- serverless-dotenv-plugin
provider:
name: aws
runtime: python3.7
lambdaHashingVersion: 20201221
profile: ${opt:profile, env:PROFILE}
stage: ${opt:stage, env:STAGE}
timeout: 5 # seconds, which is 5 seconds
iamRoleStatements:
- Effect: "Allow"
Action:
- sns:*
Resource:
- arn:aws:sns:us-east-1:540160934250:genesis-alerts
functions:
helloworld:
handler: handler.hello
destinations:
onFailure: helloFailure
helloFailure:
handler: failure_handler.handler
destinations:
onFailure: arn:aws:sns:us-east-1:540160934250:genesis-alerts
onSuccess: arn:aws:sns:us-east-1:540160934250:genesis-alerts
custom:
dotenv:
basePath: ./env/
logging: true
To test that I needed CORS enabled in my API Gateway for a Lambda Proxy, I removed the cors:true definition in my serverless.yml.
Then when I put it back, I get the following error:
You can only use "origin" or "origins", but not both at the same time to configure CORS. Please check the docs for more info.
I cant find anything in the docs that would explain why my code would be throwing that error.
I know that the single line version is synonomous with a multi line version like so:
cors:
origins:
- '*'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
allowCredentials: false
as per: https://serverless.com/framework/docs/providers/aws/events/apigateway/#enabling-cors
Does it perhaps cache the config and now it thinks Im declaring it twice, once as 'origin' and another as 'origins'.
I can't see why I would get that error if I just toggled the `cors:true' line between deploys as I did.
This is from my serverless.yml:
functions:
submitApi:
handler: handler.submit
description: Cloud API integration to the Third-Party API
events:
- http:
path: thirdParty
method: post
cors: true
Update:
This issue appears to be a bug that is being tracked in the serverless project, found here: https://github.com/serverless/serverless/issues/6098