From fbe1fb3ac7c8922b49b5b342a9a592e8154b9c4d Mon Sep 17 00:00:00 2001 From: Manasvi Jain Date: Mon, 21 Jul 2025 11:34:33 +0530 Subject: [PATCH 1/7] Final code for the pattern - Ack-connect-route --- ack-connect-route/README.md | 73 +++++++++ ack-connect-route/example-pattern.json | 44 ++++++ ack-connect-route/src/lambda_function.py | 12 ++ .../src/lambda_function_postToConnection.py | 13 ++ ack-connect-route/src/ondisconnect.py | 9 ++ ack-connect-route/template.yml | 142 ++++++++++++++++++ 6 files changed, 293 insertions(+) create mode 100644 ack-connect-route/README.md create mode 100644 ack-connect-route/example-pattern.json create mode 100644 ack-connect-route/src/lambda_function.py create mode 100644 ack-connect-route/src/lambda_function_postToConnection.py create mode 100644 ack-connect-route/src/ondisconnect.py create mode 100644 ack-connect-route/template.yml diff --git a/ack-connect-route/README.md b/ack-connect-route/README.md new file mode 100644 index 000000000..14e0406c3 --- /dev/null +++ b/ack-connect-route/README.md @@ -0,0 +1,73 @@ +## Websocket API Gateway acknowledgement for $connect route. + +The Serverless Application Model (SAM) template deploys an Amazon WebSocket API Gateway and two AWS Lambda functions. When a client connects, the API Gateway creates a $connect route with Lambda proxy integration. The first Lambda function processes the initial connection, capturing both the Connection ID and API Gateway stage URL, then asynchronously triggers the second Lambda function. This second function validates the Connection ID and, if valid, uses SDK API calls to send a greeting message back to the client. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the AWS Pricing page for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ``` + cd ack-connect-route + ``` +3. From the command line, use AWS SAM to build and deploy the AWS resources for the pattern as specified in the template.yml file: + + ``` + sam deploy --guided + ``` +4. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run guided mode once, you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## Testing + +Once the application is deployed, retrieve the WebSocketURL value from CloudFormation Outputs. To test the WebSocket API, you can use [wscat](https://github.com/websockets/wscat) which is an open-source command line tool. + +1. [Install NPM](https://www.npmjs.com/get-npm). + +2. Install wscat: + ``` + $ npm install -g wscat + ``` + +3. Connect to your WebSocketURL by executing the following command: + $ wscat -c + ``` + +4. To test the custom route and its associated function, send a JSON-formatted request. The Lambda function sends back the value of the "data" key using the callback URL: +``` +$ wscat -c +connected (press CTRL+C to quit) +``` +## Cleanup + +1. Delete the stack + ``` + aws cloudformation delete-stack --stack-name + ``` + +2. Confirm the stack has been deleted + ``` + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'')].StackStatus" + ``` + + diff --git a/ack-connect-route/example-pattern.json b/ack-connect-route/example-pattern.json new file mode 100644 index 000000000..372525845 --- /dev/null +++ b/ack-connect-route/example-pattern.json @@ -0,0 +1,44 @@ +{ + "title": "Websocket API Gateway acknowledgement for $connect route.", + "description": "The SAM template deploys a WebSocket API Gateway that creates a $connect route, triggering two Lambda functions in sequence - the first captures connection details and triggers the second asynchronously, which then validates the connection and sends a greeting via SDK API calls.", + "language": "Python", + "level": "200", + "framework": "SAM", + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/ack-connect-route", + "templateURL": "serverless-patterns/ack-connect-route", + "projectFolder": "ack-connect-route", + "templateFile": "template.yaml" + } + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Manasvi Jain", + "image": "https://avatars.githubusercontent.com/u/56217984?v=4", + "bio": "Associate Partner Solutions Architect at AWS", + "linkedin": "https://www.linkedin.com/in/manasvi-jain-36b9941a3/" + }, + { + "name": "Umang Aggarwal", + "image": "https://avatars.githubusercontent.com/Umang071", + "bio": "Technical Account Manager @ AWS", + "linkedin": "https://www.linkedin.com/in/umangaggarwal" + } + ] +} diff --git a/ack-connect-route/src/lambda_function.py b/ack-connect-route/src/lambda_function.py new file mode 100644 index 000000000..19c161652 --- /dev/null +++ b/ack-connect-route/src/lambda_function.py @@ -0,0 +1,12 @@ +import json +import boto3 + +lambdaClient = boto3.client('lambda') +def lambda_handler(event, context): + print("Got an event from some route ", event) + if event['requestContext']['routeKey'] == "$connect": + url = "https://" + event["requestContext"]["domainName"] + "/" + event["requestContext"]["stage"] + + response = lambdaClient.invoke(FunctionName="WebsocketPostToConnectionId",InvocationType="Event",Payload=json.dumps({"url": url, "connectionId":event['requestContext']['connectionId']})) + payload = "This won't be received by the client" + return {'statusCode': 200,'body': payload} \ No newline at end of file diff --git a/ack-connect-route/src/lambda_function_postToConnection.py b/ack-connect-route/src/lambda_function_postToConnection.py new file mode 100644 index 000000000..bc0271512 --- /dev/null +++ b/ack-connect-route/src/lambda_function_postToConnection.py @@ -0,0 +1,13 @@ +import json +import boto3 + +def lambda_handler(event, context): + + print("Got an event from parent lambda ", event) + gatewayapi = boto3.client("apigatewaymanagementapi",endpoint_url = event["url"]) + response = gatewayapi.get_connection(ConnectionId=event['connectionId']) + print("Response is ", response) + + if response["ResponseMetadata"]["HTTPStatusCode"] == 200: + gatewayapi.post_to_connection(ConnectionId=event['connectionId'], Data="Hello XXX, we are now connected!! ") + return {} \ No newline at end of file diff --git a/ack-connect-route/src/ondisconnect.py b/ack-connect-route/src/ondisconnect.py new file mode 100644 index 000000000..4e8ba0166 --- /dev/null +++ b/ack-connect-route/src/ondisconnect.py @@ -0,0 +1,9 @@ +import json + +def lambda_handler(event, context): + # TODO implement + print("Connection has been disconnected") + return { + 'statusCode': 200, + 'body': json.dumps('Hello from Lambda!') + } \ No newline at end of file diff --git a/ack-connect-route/template.yml b/ack-connect-route/template.yml new file mode 100644 index 000000000..2099161d0 --- /dev/null +++ b/ack-connect-route/template.yml @@ -0,0 +1,142 @@ +AWSTemplateFormatVersion: 2010-09-09 +Transform: 'AWS::Serverless-2016-10-31' +Description: An Amazon API Gateway WebSocket API and an AWS Lambda function. + +# Global values that are applied to all applicable resources in this template +Globals: + Function: + CodeUri: ./src + Runtime: python3.12 + MemorySize: 128 + Timeout: 15 + +Resources: + # API Gateway WebSocket API + WebSocketApi: + Type: 'AWS::ApiGatewayV2::Api' + Properties: + Name: !Ref AWS::StackName + Description: An Amazon API Gateway WebSocket API and an AWS Lambda function. + ProtocolType: WEBSOCKET + RouteSelectionExpression: "$request.body.action" + # Lambda Function - uses Globals to define additional configuration values + OnConnectLambdaFunction: + Type: 'AWS::Serverless::Function' + Properties: + FunctionName: !Sub '${AWS::StackName}-onconnect-function' + Handler: lambda_function.lambda_handler + MemorySize: 256 + Policies: + - Statement: + - Effect: Allow + Action: + - 'lambda:InvokeFunction' + Resource: + - !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:WebsocketPostToConnectionId' + # Function permissions grant an AWS service or another account permission to use a function + OnConnectFunctionResourcePermission: + Type: 'AWS::Lambda::Permission' + Properties: + Action: 'lambda:InvokeFunction' + Principal: apigateway.amazonaws.com + FunctionName: !Ref OnConnectLambdaFunction + SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${WebSocketApi}/*' + OnConnectIntegration: + Type: AWS::ApiGatewayV2::Integration + Properties: + ApiId: !Ref WebSocketApi + Description: OnConnect Integration + IntegrationType: AWS_PROXY + IntegrationUri: + Fn::Sub: + arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${OnConnectLambdaFunction.Arn}/invocations + OnConnectRoute: + Type: AWS::ApiGatewayV2::Route + Properties: + ApiId: !Ref WebSocketApi + RouteKey: $connect + AuthorizationType: NONE + OperationName: OnConnectRoute + Target: !Join + - '/' + - - 'integrations' + - !Ref OnConnectIntegration + # Lambda Function - uses Globals to define additional configuration values + PostLambdaFunction: + Type: 'AWS::Serverless::Function' + Properties: + FunctionName: WebsocketPostToConnectionId + Handler: lambda_function_postToConnection.lambda_handler + MemorySize: 256 + Policies: + - Statement: + - Effect: Allow + Action: + - 'execute-api:ManageConnections' + Resource: + - !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${WebSocketApi}/*' + # Function permissions grant an AWS service or another account permission to use a function + + OnDisconnectLambdaFunction: + Type: 'AWS::Serverless::Function' + Properties: + FunctionName: !Sub '${AWS::StackName}-ondisconnect-function' + Handler: ondisconnect.lambda_handler + MemorySize: 256 + # Function permissions grant an AWS service or another account permission to use a function + OnDisconnectFunctionResourcePermission: + Type: 'AWS::Lambda::Permission' + Properties: + Action: 'lambda:InvokeFunction' + Principal: apigateway.amazonaws.com + FunctionName: !Ref OnDisconnectLambdaFunction + SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${WebSocketApi}/*' + OnDisconnectIntegration: + Type: AWS::ApiGatewayV2::Integration + Properties: + ApiId: !Ref WebSocketApi + Description: OnDisconnect Integration + IntegrationType: AWS_PROXY + IntegrationUri: + Fn::Sub: + arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${OnDisconnectLambdaFunction.Arn}/invocations + OnDisconnectRoute: + Type: AWS::ApiGatewayV2::Route + Properties: + ApiId: !Ref WebSocketApi + RouteKey: $disconnect + AuthorizationType: NONE + OperationName: OnDisconnectRoute + Target: !Join + - '/' + - - 'integrations' + - !Ref OnDisconnectIntegration + Deployment: + Type: AWS::ApiGatewayV2::Deployment + DependsOn: + - OnConnectRoute + - OnDisconnectRoute + Properties: + ApiId: !Ref WebSocketApi + Stage: + Type: AWS::ApiGatewayV2::Stage + Properties: + StageName: prod + Description: Prod Stage + DeploymentId: !Ref Deployment + ApiId: !Ref WebSocketApi + +Outputs: + OnConnectLambdaFunctionArn: + Description: "OnConnect function ARN" + Value: !GetAtt OnConnectLambdaFunction.Arn + OnDisconnectLambdaFunctionArn: + Description: "OnDisconnect function ARN" + Value: !GetAtt OnDisconnectLambdaFunction.Arn + PostLambdaFunctionArn: + Description: "Post function ARN" + Value: !GetAtt PostLambdaFunction.Arn + WebSocketURL: + Description: "The WSS Protocol URL to connect to" + Value: !Join [ '', [ 'wss://', !Ref WebSocketApi, '.execute-api.',!Ref 'AWS::Region','.amazonaws.com/',!Ref 'Stage'] ] + From 460b9dcef483423accdbd9c9bde28576be333753 Mon Sep 17 00:00:00 2001 From: Manasvi Jain Date: Mon, 21 Jul 2025 11:34:33 +0530 Subject: [PATCH 2/7] Final code for the pattern - Ack-connect-route --- ack-connect-route/README.md | 73 +++++++++ ack-connect-route/example-pattern.json | 44 ++++++ ack-connect-route/src/lambda_function.py | 12 ++ .../src/lambda_function_postToConnection.py | 13 ++ ack-connect-route/src/ondisconnect.py | 9 ++ ack-connect-route/template.yml | 142 ++++++++++++++++++ 6 files changed, 293 insertions(+) create mode 100644 ack-connect-route/README.md create mode 100644 ack-connect-route/example-pattern.json create mode 100644 ack-connect-route/src/lambda_function.py create mode 100644 ack-connect-route/src/lambda_function_postToConnection.py create mode 100644 ack-connect-route/src/ondisconnect.py create mode 100644 ack-connect-route/template.yml diff --git a/ack-connect-route/README.md b/ack-connect-route/README.md new file mode 100644 index 000000000..14e0406c3 --- /dev/null +++ b/ack-connect-route/README.md @@ -0,0 +1,73 @@ +## Websocket API Gateway acknowledgement for $connect route. + +The Serverless Application Model (SAM) template deploys an Amazon WebSocket API Gateway and two AWS Lambda functions. When a client connects, the API Gateway creates a $connect route with Lambda proxy integration. The first Lambda function processes the initial connection, capturing both the Connection ID and API Gateway stage URL, then asynchronously triggers the second Lambda function. This second function validates the Connection ID and, if valid, uses SDK API calls to send a greeting message back to the client. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the AWS Pricing page for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ``` + cd ack-connect-route + ``` +3. From the command line, use AWS SAM to build and deploy the AWS resources for the pattern as specified in the template.yml file: + + ``` + sam deploy --guided + ``` +4. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run guided mode once, you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## Testing + +Once the application is deployed, retrieve the WebSocketURL value from CloudFormation Outputs. To test the WebSocket API, you can use [wscat](https://github.com/websockets/wscat) which is an open-source command line tool. + +1. [Install NPM](https://www.npmjs.com/get-npm). + +2. Install wscat: + ``` + $ npm install -g wscat + ``` + +3. Connect to your WebSocketURL by executing the following command: + $ wscat -c + ``` + +4. To test the custom route and its associated function, send a JSON-formatted request. The Lambda function sends back the value of the "data" key using the callback URL: +``` +$ wscat -c +connected (press CTRL+C to quit) +``` +## Cleanup + +1. Delete the stack + ``` + aws cloudformation delete-stack --stack-name + ``` + +2. Confirm the stack has been deleted + ``` + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'')].StackStatus" + ``` + + diff --git a/ack-connect-route/example-pattern.json b/ack-connect-route/example-pattern.json new file mode 100644 index 000000000..372525845 --- /dev/null +++ b/ack-connect-route/example-pattern.json @@ -0,0 +1,44 @@ +{ + "title": "Websocket API Gateway acknowledgement for $connect route.", + "description": "The SAM template deploys a WebSocket API Gateway that creates a $connect route, triggering two Lambda functions in sequence - the first captures connection details and triggers the second asynchronously, which then validates the connection and sends a greeting via SDK API calls.", + "language": "Python", + "level": "200", + "framework": "SAM", + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/ack-connect-route", + "templateURL": "serverless-patterns/ack-connect-route", + "projectFolder": "ack-connect-route", + "templateFile": "template.yaml" + } + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Manasvi Jain", + "image": "https://avatars.githubusercontent.com/u/56217984?v=4", + "bio": "Associate Partner Solutions Architect at AWS", + "linkedin": "https://www.linkedin.com/in/manasvi-jain-36b9941a3/" + }, + { + "name": "Umang Aggarwal", + "image": "https://avatars.githubusercontent.com/Umang071", + "bio": "Technical Account Manager @ AWS", + "linkedin": "https://www.linkedin.com/in/umangaggarwal" + } + ] +} diff --git a/ack-connect-route/src/lambda_function.py b/ack-connect-route/src/lambda_function.py new file mode 100644 index 000000000..19c161652 --- /dev/null +++ b/ack-connect-route/src/lambda_function.py @@ -0,0 +1,12 @@ +import json +import boto3 + +lambdaClient = boto3.client('lambda') +def lambda_handler(event, context): + print("Got an event from some route ", event) + if event['requestContext']['routeKey'] == "$connect": + url = "https://" + event["requestContext"]["domainName"] + "/" + event["requestContext"]["stage"] + + response = lambdaClient.invoke(FunctionName="WebsocketPostToConnectionId",InvocationType="Event",Payload=json.dumps({"url": url, "connectionId":event['requestContext']['connectionId']})) + payload = "This won't be received by the client" + return {'statusCode': 200,'body': payload} \ No newline at end of file diff --git a/ack-connect-route/src/lambda_function_postToConnection.py b/ack-connect-route/src/lambda_function_postToConnection.py new file mode 100644 index 000000000..bc0271512 --- /dev/null +++ b/ack-connect-route/src/lambda_function_postToConnection.py @@ -0,0 +1,13 @@ +import json +import boto3 + +def lambda_handler(event, context): + + print("Got an event from parent lambda ", event) + gatewayapi = boto3.client("apigatewaymanagementapi",endpoint_url = event["url"]) + response = gatewayapi.get_connection(ConnectionId=event['connectionId']) + print("Response is ", response) + + if response["ResponseMetadata"]["HTTPStatusCode"] == 200: + gatewayapi.post_to_connection(ConnectionId=event['connectionId'], Data="Hello XXX, we are now connected!! ") + return {} \ No newline at end of file diff --git a/ack-connect-route/src/ondisconnect.py b/ack-connect-route/src/ondisconnect.py new file mode 100644 index 000000000..4e8ba0166 --- /dev/null +++ b/ack-connect-route/src/ondisconnect.py @@ -0,0 +1,9 @@ +import json + +def lambda_handler(event, context): + # TODO implement + print("Connection has been disconnected") + return { + 'statusCode': 200, + 'body': json.dumps('Hello from Lambda!') + } \ No newline at end of file diff --git a/ack-connect-route/template.yml b/ack-connect-route/template.yml new file mode 100644 index 000000000..2099161d0 --- /dev/null +++ b/ack-connect-route/template.yml @@ -0,0 +1,142 @@ +AWSTemplateFormatVersion: 2010-09-09 +Transform: 'AWS::Serverless-2016-10-31' +Description: An Amazon API Gateway WebSocket API and an AWS Lambda function. + +# Global values that are applied to all applicable resources in this template +Globals: + Function: + CodeUri: ./src + Runtime: python3.12 + MemorySize: 128 + Timeout: 15 + +Resources: + # API Gateway WebSocket API + WebSocketApi: + Type: 'AWS::ApiGatewayV2::Api' + Properties: + Name: !Ref AWS::StackName + Description: An Amazon API Gateway WebSocket API and an AWS Lambda function. + ProtocolType: WEBSOCKET + RouteSelectionExpression: "$request.body.action" + # Lambda Function - uses Globals to define additional configuration values + OnConnectLambdaFunction: + Type: 'AWS::Serverless::Function' + Properties: + FunctionName: !Sub '${AWS::StackName}-onconnect-function' + Handler: lambda_function.lambda_handler + MemorySize: 256 + Policies: + - Statement: + - Effect: Allow + Action: + - 'lambda:InvokeFunction' + Resource: + - !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:WebsocketPostToConnectionId' + # Function permissions grant an AWS service or another account permission to use a function + OnConnectFunctionResourcePermission: + Type: 'AWS::Lambda::Permission' + Properties: + Action: 'lambda:InvokeFunction' + Principal: apigateway.amazonaws.com + FunctionName: !Ref OnConnectLambdaFunction + SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${WebSocketApi}/*' + OnConnectIntegration: + Type: AWS::ApiGatewayV2::Integration + Properties: + ApiId: !Ref WebSocketApi + Description: OnConnect Integration + IntegrationType: AWS_PROXY + IntegrationUri: + Fn::Sub: + arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${OnConnectLambdaFunction.Arn}/invocations + OnConnectRoute: + Type: AWS::ApiGatewayV2::Route + Properties: + ApiId: !Ref WebSocketApi + RouteKey: $connect + AuthorizationType: NONE + OperationName: OnConnectRoute + Target: !Join + - '/' + - - 'integrations' + - !Ref OnConnectIntegration + # Lambda Function - uses Globals to define additional configuration values + PostLambdaFunction: + Type: 'AWS::Serverless::Function' + Properties: + FunctionName: WebsocketPostToConnectionId + Handler: lambda_function_postToConnection.lambda_handler + MemorySize: 256 + Policies: + - Statement: + - Effect: Allow + Action: + - 'execute-api:ManageConnections' + Resource: + - !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${WebSocketApi}/*' + # Function permissions grant an AWS service or another account permission to use a function + + OnDisconnectLambdaFunction: + Type: 'AWS::Serverless::Function' + Properties: + FunctionName: !Sub '${AWS::StackName}-ondisconnect-function' + Handler: ondisconnect.lambda_handler + MemorySize: 256 + # Function permissions grant an AWS service or another account permission to use a function + OnDisconnectFunctionResourcePermission: + Type: 'AWS::Lambda::Permission' + Properties: + Action: 'lambda:InvokeFunction' + Principal: apigateway.amazonaws.com + FunctionName: !Ref OnDisconnectLambdaFunction + SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${WebSocketApi}/*' + OnDisconnectIntegration: + Type: AWS::ApiGatewayV2::Integration + Properties: + ApiId: !Ref WebSocketApi + Description: OnDisconnect Integration + IntegrationType: AWS_PROXY + IntegrationUri: + Fn::Sub: + arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${OnDisconnectLambdaFunction.Arn}/invocations + OnDisconnectRoute: + Type: AWS::ApiGatewayV2::Route + Properties: + ApiId: !Ref WebSocketApi + RouteKey: $disconnect + AuthorizationType: NONE + OperationName: OnDisconnectRoute + Target: !Join + - '/' + - - 'integrations' + - !Ref OnDisconnectIntegration + Deployment: + Type: AWS::ApiGatewayV2::Deployment + DependsOn: + - OnConnectRoute + - OnDisconnectRoute + Properties: + ApiId: !Ref WebSocketApi + Stage: + Type: AWS::ApiGatewayV2::Stage + Properties: + StageName: prod + Description: Prod Stage + DeploymentId: !Ref Deployment + ApiId: !Ref WebSocketApi + +Outputs: + OnConnectLambdaFunctionArn: + Description: "OnConnect function ARN" + Value: !GetAtt OnConnectLambdaFunction.Arn + OnDisconnectLambdaFunctionArn: + Description: "OnDisconnect function ARN" + Value: !GetAtt OnDisconnectLambdaFunction.Arn + PostLambdaFunctionArn: + Description: "Post function ARN" + Value: !GetAtt PostLambdaFunction.Arn + WebSocketURL: + Description: "The WSS Protocol URL to connect to" + Value: !Join [ '', [ 'wss://', !Ref WebSocketApi, '.execute-api.',!Ref 'AWS::Region','.amazonaws.com/',!Ref 'Stage'] ] + From ed01dee999d692290e0882f87396e45f551d70dc Mon Sep 17 00:00:00 2001 From: Manasvi Jain <56217984+manasvijain99@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:16:06 +0530 Subject: [PATCH 3/7] Update ack-connect-route/README.md Co-authored-by: ellisms <114107920+ellisms@users.noreply.github.com> --- ack-connect-route/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ack-connect-route/README.md b/ack-connect-route/README.md index 14e0406c3..34d3ca3b3 100644 --- a/ack-connect-route/README.md +++ b/ack-connect-route/README.md @@ -1,4 +1,4 @@ -## Websocket API Gateway acknowledgement for $connect route. +## Websocket acknowledgement for $connect route in Amazon API Gateway The Serverless Application Model (SAM) template deploys an Amazon WebSocket API Gateway and two AWS Lambda functions. When a client connects, the API Gateway creates a $connect route with Lambda proxy integration. The first Lambda function processes the initial connection, capturing both the Connection ID and API Gateway stage URL, then asynchronously triggers the second Lambda function. This second function validates the Connection ID and, if valid, uses SDK API calls to send a greeting message back to the client. From 7289b2d93c1a7a7ec451816ca163ece894114828 Mon Sep 17 00:00:00 2001 From: Manasvi Jain <56217984+manasvijain99@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:16:15 +0530 Subject: [PATCH 4/7] Update ack-connect-route/README.md Co-authored-by: ellisms <114107920+ellisms@users.noreply.github.com> --- ack-connect-route/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ack-connect-route/README.md b/ack-connect-route/README.md index 34d3ca3b3..79ae01f9c 100644 --- a/ack-connect-route/README.md +++ b/ack-connect-route/README.md @@ -1,6 +1,6 @@ ## Websocket acknowledgement for $connect route in Amazon API Gateway -The Serverless Application Model (SAM) template deploys an Amazon WebSocket API Gateway and two AWS Lambda functions. When a client connects, the API Gateway creates a $connect route with Lambda proxy integration. The first Lambda function processes the initial connection, capturing both the Connection ID and API Gateway stage URL, then asynchronously triggers the second Lambda function. This second function validates the Connection ID and, if valid, uses SDK API calls to send a greeting message back to the client. +The Serverless Application Model (SAM) template deploys an Amazon WebSocket API Gateway and two AWS Lambda functions. When a client connects, the API Gateway creates a $connect route with Lambda proxy integration. The first Lambda function processes the initial connection, capturing both the Connection ID and API Gateway stage URL, then asynchronously invokes the second Lambda function. This second function validates the Connection ID and, if valid, uses SDK API calls to send a greeting message back to the client. Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns From c17da31c03486302f149bbe79d829b79011a5c3f Mon Sep 17 00:00:00 2001 From: Manasvi Jain <56217984+manasvijain99@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:16:28 +0530 Subject: [PATCH 5/7] Update ack-connect-route/README.md Co-authored-by: ellisms <114107920+ellisms@users.noreply.github.com> --- ack-connect-route/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ack-connect-route/README.md b/ack-connect-route/README.md index 79ae01f9c..d620c81c1 100644 --- a/ack-connect-route/README.md +++ b/ack-connect-route/README.md @@ -34,7 +34,7 @@ Important: this application uses various AWS services and there are costs associ * Enter the desired AWS Region * Allow SAM CLI to create IAM roles with the required permissions. - Once you have run guided mode once, you can use `sam deploy` in future to use these defaults. + After running guided mode once, you can use `sam deploy` in future to use these defaults. 1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. From 218a7c209a11f5a28e854f69381e5c7ff806e74f Mon Sep 17 00:00:00 2001 From: Manasvi Jain <56217984+manasvijain99@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:16:36 +0530 Subject: [PATCH 6/7] Update ack-connect-route/README.md Co-authored-by: ellisms <114107920+ellisms@users.noreply.github.com> --- ack-connect-route/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ack-connect-route/README.md b/ack-connect-route/README.md index d620c81c1..93dc4dbf3 100644 --- a/ack-connect-route/README.md +++ b/ack-connect-route/README.md @@ -40,7 +40,7 @@ Important: this application uses various AWS services and there are costs associ ## Testing -Once the application is deployed, retrieve the WebSocketURL value from CloudFormation Outputs. To test the WebSocket API, you can use [wscat](https://github.com/websockets/wscat) which is an open-source command line tool. +After deployment, retrieve the `WebSocketURL` value from CloudFormation Outputs. To test the WebSocket API, you can use [wscat](https://github.com/websockets/wscat) which is an open-source command line tool. 1. [Install NPM](https://www.npmjs.com/get-npm). From 56b2bda330be5c45852399a3efbee78cc2647836 Mon Sep 17 00:00:00 2001 From: ellisms <114107920+ellisms@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:52:21 -0400 Subject: [PATCH 7/7] adding publishing file --- ack-connect-route/ack-connect-route.json | 80 ++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 ack-connect-route/ack-connect-route.json diff --git a/ack-connect-route/ack-connect-route.json b/ack-connect-route/ack-connect-route.json new file mode 100644 index 000000000..1049c2850 --- /dev/null +++ b/ack-connect-route/ack-connect-route.json @@ -0,0 +1,80 @@ +{ + "title": "Amazon API Gateway WebSocket acknowledgement for $connect route.", + "description": "This pattern demonstrates acknowledging WebSocket connections with API Gateway and AWS Lambda", + "language": "Python", + "level": "200", + "framework": "AWS SAM", + "introBox": { + "headline": "How it works", + "text": [ + "The Serverless Application Model (SAM) template deploys an Amazon WebSocket API Gateway and two AWS Lambda functions. When a client connects, the API Gateway creates a $connect route with Lambda proxy integration. The first Lambda function processes the initial connection, capturing both the Connection ID and API Gateway stage URL, then asynchronously invokes the second Lambda function. This second function validates the Connection ID and, if valid, uses SDK API calls to send a greeting message back to the client." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/ack-connect-route", + "templateURL": "serverless-patterns/ack-connect-route", + "projectFolder": "ack-connect-route", + "templateFile": "template.yml" + } + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Manasvi Jain", + "image": "https://avatars.githubusercontent.com/u/56217984?v=4", + "bio": "Associate Partner Solutions Architect at AWS", + "linkedin": "manasvi-jain-36b9941a3" + }, + { + "name": "Umang Aggarwal", + "image": "https://avatars.githubusercontent.com/Umang071", + "bio": "Technical Account Manager @ AWS", + "linkedin": "/umangaggarwal" + } + ], + "patternArch": { + "icon1": { + "x": 20, + "y": 50, + "service": "apigw", + "label": "API Gateway WebSocket API" + }, + "icon2": { + "x": 50, + "y": 50, + "service": "lambda", + "label": "AWS Lambda" + }, + "icon3": { + "x": 90, + "y": 50, + "service": "lambda", + "label": "AWS Lambda" + }, + "line1": { + "from": "icon1", + "to": "icon2", + "label": "Validation" + }, + "line2": { + "from": "icon2", + "to": "icon3", + "label": "Generate Response" + } + } +}