[PYTHON] Let's create a chat function with Vue.js + AWS Lambda + dynamo DB [AWS settings]

Since we implemented the chat function this time, we will leave an article as a memorandum.

After creating AWS Lambda, I will post an article on front side settings. The AWS side will be created with the configuration of ʻAPI Gateway + Lambda + DynamoDB`.

API Gateway settings

① Create API from API Gateway

From API Gateway, press ʻCreate API` and press We will create a WebSocket API. 1_API_gateway.png 2_API_gateway.png

② Set API and route selection formula

Set the protocol with the following contents.

Protocol: WebSocket

item Contents
API name vue_chat_test
Route selection formula $request.body.action
Description vue_chat_test

3_プロトコル-2.png

③ Set the root key

After creating the API, the screen will change to the screen for creating the root key. This time, we will set each root key below.

Key name Use
$connect Used when the client starts WebSocket communication
$disconnect Used when the client terminates WebSocket communication
$default Used when there is no corresponding root key
sendmessage Used when sending a message
getmessages Used when getting a message

However, since you can not create a root key unless you have created a Lambda function, First, create the dynamoDB table and Lambda function.

Create table with dynamoDB

dynamoDB will be created with the following contents. The configuration is an assumption of a chat where you can see the messages that have arrived to you.

item Contents Key setting
connection_id Connection ID Partition key
user_id User ID
  • messages
item Contents Key setting
user_id Connection ID Partition key
created_at date of creation Sort key
message message
  • When actually creating as a chat function I think you need information such as room ID, who sent it to whom, and read information, This time it is a simple chat function, so I will make it only with this information.

Create a Lambda function

The runtime is Python3.8 and creates the following Lambda function.

function Contents Access authority setting
OnConnect In the connection table
Save connection ID / user ID
dynamoDB
OnDisconnect Delete record in connection table dynamoDB
SendMessage Save message dynamoDB,ExecuteAPI
GetMessages Get message dynamoDB,ExecuteAPI
  • Access authority is once set with full access permission
  • OnConnect
import json
import boto3

#Get dynamoDB table
dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table('connections')

def lambda_handler(event, context):

    #Get "connectionId, userId"
    connection_id = event.get('requestContext',{}).get('connectionId')
    user_id = event.get('queryStringParameters',{}).get('userId')

    #Add to connections table
    result = connections.put_item(
        Item={
            'connection_id': connection_id,
            'user_id': str(user_id)
        }
    )
    return { 'statusCode': 200,'body': 'ok' }
  • OnDisconnect
import json
import boto3

dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table('connections')

def lambda_handler(event, context):
    connection_id = event.get('requestContext',{}).get('connectionId')
    result = connections.delete_item(Key={ 'connection_id': connection_id })
    return { 'statusCode': 200, 'body': 'ok' }
  • SendMessage
import json
import boto3
import json
from boto3.dynamodb.conditions import Key, Attr
from datetime import datetime, timedelta, timezone

#Get dynamoDB table
dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table('connections')
messages = dynamodb.Table('messages')

#TimeZone settings
JST = timezone(timedelta(hours=+9), 'JST')

def lambda_handler(event, context):

	#Parameter acquisition
    body = json.loads(event["body"])
    user_id = body['user_id']
    message = body['message']
    
    #Creation date and time
    now = "{0:%Y-%m-%d %H:%M:%S}".format(datetime.now(JST))

    #Save to message table
    messages.put_item(
        Item={
            'user_id': user_id,
            'message': message,
            'created_at': now
        }
    )

    #ApiGatewayManagementApi settings
    domain_name   = event.get('requestContext',{}).get('domainName')
    stage         = event.get('requestContext',{}).get('stage')
    apigw_management = boto3.client('apigatewaymanagementapi',
                                    endpoint_url=F"https://{domain_name}/{stage}")
                                   
    #Destination user connection_Get id
    connection_items = connections.scan(
		ProjectionExpression="connection_id",
        FilterExpression=Attr("user_id").eq(str(user_id))
	).get('Items')    
 
    if len(connection_items) == 0:
	    return { 'statusCode': 200,'body': 'ok' }

    #Get message information
    get_message_items = messages.query(
        KeyConditionExpression=Key("user_id").eq(str(user_id)) & Key("created_at").lte(now),
        ScanIndexForward=True
    ).get('Items')

    #Set message if there is a message, empty array if not
    content_all = [] if len(get_message_items) == 0 else get_message_items

	#Return message to destination user
    for item in connection_items:
        _ = apigw_management.post_to_connection(ConnectionId=item['connection_id'],
                                                    Data=json.dumps(content_all))

    return { 'statusCode': 200,'body': 'ok' }
  • GetMessages
import boto3
import json
from boto3.dynamodb.conditions import Key, Attr
from datetime import datetime, timedelta, timezone


#table definition
dynamodb_client = boto3.client('dynamodb')
dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table('connections')
messages = dynamodb.Table('messages')

#TimeZone settings
JST = timezone(timedelta(hours=+9), 'JST')

def lambda_handler(event, context):

    #Set parameter information to variable
    body = json.loads(event["body"])
    user_id = body['user_id']
    connection_id = event.get('requestContext',{}).get('connectionId')
    domain_name = event.get('requestContext',{}).get('domainName')
    stage       = event.get('requestContext',{}).get('stage')

    #Check if the connection ID matches
    connection_items = connections.query(
        ProjectionExpression="connection_id",
        KeyConditionExpression=Key("connection_id").eq(connection_id),
        FilterExpression=Key('user_id').eq(str(user_id)),
    ).get('Items')

    if len(connection_items) == 0:
        return { 'statusCode': 500,'body': 'something went wrong' }
    
    #ApiGatewayManagementApi settings
    apigw_management = boto3.client('apigatewaymanagementapi',
                                    endpoint_url=F"https://{domain_name}/{stage}")
    
    #Acquisition date and time
    now = "{0:%Y-%m-%d %H:%M:%S}".format(datetime.now(JST))

    #Get message information
    get_message_items = messages.query(
        KeyConditionExpression=Key("user_id").eq(str(user_id)) & Key("created_at").lte(now),
        ScanIndexForward=True
    ).get('Items')

    #Set message if there is a message, empty array if not
    content_all = [] if len(get_message_items) == 0 else get_message_items

    #POST a message to the connection
    _ = apigw_management.post_to_connection(
        ConnectionId=connection_id,
        Data=json.dumps(content_all)
    )
    
    return {
        'statusCode': 200,
        'body': 'ok'
    }

Linking root key and Lambda

We will link the created Lambda with the root key of API Gateway.

Root key Lambda function
$connect OnConnect
$disconnect OnDisconnect
$default OnConnect
sendmessage SendMessage
getmessages GetMessages

4_ルートキーとLambdaの紐付け.png

Deploy the created WebSocket API

After linking the root key, deploy the WebSocket API. This time, the stage name is created with Test.

5_デプロイ.png

When the deployment is complete, you will be issued a WebSocket URL and a connection URL. This is the URL required for communication with the front desk.

That's all for the settings on the AWS side, and finally we will only check the operation.

Confirmed operation of WebSocket API with wscat

install wscat

$ npm install -g wscat

Use wscat to check the operation and confirm that the data is saved in dynamoDB.

$ wscat -c [WebSocket URL]?userId=A

Connected (press CTRL+C to quit)
> {"action":"sendmessage","user_id": "A", "message": "Test"}
[{"message": "Test", "user_id": "A", "created_at": "2020-08-29 17:22:43"}]

> {"action":"getmessages", "user_id": "A"}
[{"message": "Test", "user_id": "A", "created_at": "2020-08-29 17:22:43"}]

Click here for the status of dynamoDB!

スクリーンショット 2020-08-29 17.29.17.png スクリーンショット 2020-08-29 17.29.21.png

Summary

I tried to create AWS settings for chat function with Vue.js + AWS Lambda + dynamo DB. This time, I mainly created it to work, so I think that there is not much processing in case of an error, and there are many other things to think about in order to actually implement it.

Next, we will create a front screen using Vue.js and communicate with the API.

  • Posting date and time is undecided

If you have any suggestions, please comment!

Recommended Posts