Configuring Serverless to handle required path parameters - node.js

I'm new to serverless and writing my first service. It is built for the AWS API gateway and node.js lambda functions. Consider this as my serverless.yaml file:
service: applicationCatalog
frameworkVersion: '2'
provider:
name: aws
runtime: nodejs12.x
functions:
listShirts:
handler: handler.listShirts
events:
- httpApi: GET /
createShirt:
handler: handler.createShirt
events:
- httpApi: POST /
getShirt:
handler: handler.getShirt
events:
- httpApi:
method: GET
path: "/{shirtId}"
request:
parameters:
paths:
shirtId: true
deleteShirt:
handler: handler.deleteShirt
events:
- httpApi:
method: DELETE
path: "/{shirtId}"
request:
parameters:
paths:
shirtId: true
resources: {}
The functions listShirts, createShirt, and getShirt all work as I expected, and deleteShirt works too when a ShirtId is passed. The issue is when I don't pass the ShirtId on a delete. Assuming my service url is "https://shirts.mywardrobeapi.com". I'd expect this request:
DELETE https://shirts.mywardrobeapi.com
to trigger an error response from the API gateway. Instead, the deleteShirt function is invoked. Of course I could handle this simple check inside the function, but I thought that's what the { "shirtId" : true } setting in the serverless.yaml file was for. How can I get this setting to treat shirtId as required and not invoke the function when it's not provided? If I can't, what is this purpose of this setting?

I would suggest using Middy and validator middleware for handling required parameters.
Yep, the disadvantage is that your lambda is triggered all the time. But also you obtain
the flexibility of keeping handling required params in the code
clear logging if the parameters were wrong
you may also validate outgoing responses
keeps you serverless.yaml cleaner
We prefer the middy more than precise configure of Gateway API.

Related

Serverless Deploy command causing 'cannot resolve' DB connection string error

I have a serverless API that's no longer connecting to the DB. Initially when it was built, the DB was a self-hosted instance in AWS. I recently moved the DB to Atlas and now the API can no longer connect to the DB.
When sending queries via postman, I'm getting invalid credentials error. I'm assuming the error is due to some outdated packages. So I updated them, pushed the code to the repo, then the pipeline zipped it up and stored into S3. Now when I run sls deploy I get the following error:
Cannot resolve serverless.yml: Variables resolution errored with:
- Cannot resolve variable at "provider.environment.MONGODB_URI": Source "file" returned not supported result: "undefined"
The connection to the DB is a Lambda Function, so I'm not sure why its looking locally for the connection string and not for the lambda function.
My serverless.yml file looks like this:
service: aws-lambda-api-service
variablesResolutionMode: 20210326
frameworkVersion: '2'
provider:
name: aws
runtime: nodejs12.x
lambdaHashingVersion: 20201221
stage: dev
region: us-west-1
environment:
MONGODB_URI: ${file(./config.js):fetchMongoUrl}
and my config.js file looks like this:
module.exports.fetchMongoUrl = () => {
// create / fetch dynamic data here (e.g. call an API)
return process.env.MONGODB_URI;
I understand that this is sort of a loop that doesn't contain the connection string. My question is, what do I need to do for it to look for the lambda function that handles the connection?
I just want to deploy the updated packages in the zip file so that I can see if it fixes other connection errors I'm receiving when attempting to send queries via Postman.
Any help will be greatly appreciated.

Message Hub as event source in Serverless project doesn't create any triggers or rules

I'm trying to set up a Message Hub topic as an event source for the cloud function like so:
custom:
org: MyOrganization
space: dev
mhServiceName: my-kafka-service
functions:
main:
handler: src/handler.main
events:
- message_hub:
package: /${self:custom.org}_${self:custom.space}/Bluemix_${self:custom.mhServiceName}_Credentials-1
topic: test_topic
When I deploy the service, there are no triggers or rules being created. Thus the function is not being invoked when I publish messages to the Kafka topic.
I also tried to explicitly set a trigger and rule, but that only creates a trigger of type custom, instead of type message hub. Custom triggers seem to not work in this scenario.
What am I missing?
Update
As James pointed out, the reason the triggers and rules were not created was due to the fact, that the indentation wasn't correct.
I was still running into problems with the package not being found (see my answer to James solution) when trying to deploy the function and I've found out what the problem was there.
Turns out, you have to do two more things that are not explicitly mentioned in the documentation.
1) You have to manually create service credentials (the documentation assumes you called them Credentials-1 so I did the same)
2) You have to bind Kafka (Message Hub, now called Event Streams) to your function in your serverless.yml
The resulting function definition should look like this:
functions:
main:
handler: src/handler.main
bind:
- service:
name: messagehub
instance: ${self:custom.mhServiceName}
events:
- message_hub:
package: /${self:custom.org}_${self:custom.space}/Bluemix_${self:custom.mhServiceName}_Credentials-1
topic: test_topic
The YAML indentation on the serverless.yml is incorrect. This means the event properties aren't registered by the framework during deployment.
Change the serverless.yml file to the following format and it should work.
custom:
org: MyOrganization
space: dev
mhServiceName: my-kafka-service
functions:
main:
handler: src/handler.main
events:
- message_hub:
package: /${self:custom.org}_${self:custom.space}/Bluemix_${self:custom.mhServiceName}_Credentials-1
topic: test_topic

AWS SAM FindInMap Not Populating Variable

I am trying to get a simple SAM template to populate environmental variables "dynamically" using the !FindInMap intrinsic function. I have followed many examples, including AWS's documentation, without any luck. For some reason the function will not populate environment variables using it even though everything seems to be correct. It will just set the variable to an empty string.
You can see from the code below that I am using a !Ref function inside of it, but have tried hardcoding the parameters of the function without any luck. You'll also notice that the function is in the Global section, and you may think it's not working because it's there and not function environmentals, but I've tried both with neither of them working. You'll also notice that I am populating a environment variable called STAGE which is working correctly and setting it to "local".
I am testing the function by running sam start local-api and outputting the environment variables in the response.
Any suggestions would be very helpful.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: "Test Server"
Parameters:
Environment:
Type: String
Default: local
AllowedValues:
- local
- test
- prod
Mappings:
EnvParams:
local:
stage: "local"
databaseUrl: "mongodb://localhost:32768/test"
Globals:
Function:
Timeout: 500
Runtime: nodejs8.10
Environment:
Variables:
STAGE: !Ref Environment
DB_URL: !FindInMap [EnvParams, !Ref Environment, databaseUrl]
Resources:
ArticlesGetFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: src/articles/
Handler: index.getById
Events:
HelloWorld:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /api/article/
Method: get
Outputs:
HelloWorldApi:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
HelloWorldFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt HelloWorldFunction.Arn
HelloWorldFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt HelloWorldFunctionRole.Arn
It looks like !FindInMap isn't supported in local debugging yet. Here's the relevant GitHub issue:
https://github.com/awslabs/aws-sam-cli/issues/476
To set and test Environment Variables in SAM CLI, you can use the --env-vars option instead. !FindInMap is also supported when deployed via CloudFormation, you could test this feature by deploying a simple Lambda function and running a test query against it.
I had similar error because of this:
!FindInMap [EnvMap, !Ref Stage, dbpass] - correct
!FindInMap [EnvMap, !Ref Stage, dbpass] - error

BadRequestException: Lex is unable to access the Lambda function

Hi I am working building chatbot using Aws Lex and lambda.
I am using lexmodelbuildingservice.putIntent API to create new Intent. But when i test my Lambda function. I get below error:
BadRequestException: Lex is unable to access the Lambda function arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:chatBotResponse in the context of intent arn:aws:lex:us-east-1:XXXXXXXXXXXX:intent:BenefitsTwotwo:$LATEST. Please check the resource-based policy on the function
I am using below code in my params:
fulfillmentActivity: {
codeHook: {
uri: "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:chatBotResponse",
messageVersion: "1.0"
},
type: "CodeHook"
}
Can anybody please tell me how to access and enable lambda arn using nodejs.
Thanks!
BadRequestException
Request validation failed, there is no usable message in the context, or the bot build failed, is still in progress, or contains unbuilt changes.
HTTP Status Code: 400
Did your bot completely built? or contains unbuilt changes?

serverless events are missing

I have a problem with my serverless configuration resulting in lambda functions being deployed without their triggers.
I have a main serverless.yml for my skills, as below:
service: ${file(./${env:DEPLOY_FILE_NAME}):service}
provider:
name: aws
custom:
globalSchedule: ${file(./${env:DEPLOY_FILE_NAME_STAGE}):globalSchedule}
roleName: ${file(./${env:DEPLOY_FILE_NAME_STAGE}):roleName}
profileName: ${file(./${env:DEPLOY_FILE_NAME_STAGE}):profileName}
plugins:
- pluginHandler
runtime: nodejs4.3
cfLogs: true
stage: ${file(./${env:DEPLOY_FILE_NAME_STAGE}):stage}
region: ${file(./${env:DEPLOY_FILE_NAME_STAGE}):region}
memorySize: ${file(./${env:DEPLOY_FILE_NAME_STAGE}):memorySize}
timeout: ${file(./${env:DEPLOY_FILE_NAME_STAGE}):timeout}
keepWarm: false
useApigateway: false
events:
${file(./${env:DEPLOY_FILE_NAME}):events}
package:
exclude:
${file(./${env:DEPLOY_FILE_NAME}):exclude}
functions:
smartHome:
handler: ${file(./${env:DEPLOY_FILE_NAME_STAGE}):handler}
Then, I have two sets of yaml settings files. One for ${skill_type}_${localization} ie customskill_eu.yml and another stage-specific ${skill_type}${localization}{$stage} like smarthome_us_dev.yml etc.
service: alexa-SmartHomeSkillAdapter
exclude:
- app.js
- .idea/**
- .npmignore/**
- .jshintrc
- build/**
- documentation.docx
- dist/**
- event.json
- lambda_function_custom_skill.js
- resources/**
- custom_skill_eu.yml
- custom_skill_us.yml
- smart_home_eu.yml
- smart_home_us.yml
- serverless_settings/**
- tests/**
events:
- s3: ${file(./${env:DEPLOY_FILE_NAME_STAGE}):s3}
- alexaSmartHome: amzn1.ask.skill.d48263be-c7ef-4d61-a773-d6431567e6d6
What is wrong? Please advise.
Thank you.
You need to add events to your functions. Have a read through the serverless documentation for events.
Currently serverless supports lambdas to be invoked by API GateWay, Kinesis, DynamoDB, S3, Schedule, SNS, and Alexa Skill. (read more)
So in this case, adding a required events tag should solve your problem.
...
functions:
smartHome:
handler: ${file(./${env:DEPLOY_FILE_NAME_STAGE}):handler}
events: ${file(./${env:DEPLOY_FILE_NAME}):events}
...
Alternatively, you can always define all resources and their actions using traditional CloudFormation format within serverless resources node.

Resources