0
votes

I have deployed a serverless app to aws with serverless framework. I have a simple todo application.

TodoItem looks like this:

export interface TodoItem {
  userId: string
  todoId: string
  createdAt: string
  name: string
  dueDate: string
  done: boolean
  attachmentUrl?: string
}

This is how I have set up the dynamodb:

TodosDynamoDBTable:
      Type: 'AWS::DynamoDB::Table'
      Properties:
        AttributeDefinitions:
          - AttributeName: userId
            AttributeType: S
          - AttributeName: todoId
            AttributeType: S
          - AttributeName: createdAt
            AttributeType: S
        KeySchema:
          - AttributeName: todoId
            KeyType: HASH
          - AttributeName: createdAt
            KeyType: RANGE
        BillingMode: PAY_PER_REQUEST
        TableName: ${self:provider.environment.TODOS_TABLE}
        GlobalSecondaryIndexes:
          - IndexName: ${self:provider.environment.USER_ID_INDEX}
            KeySchema:
              - AttributeName: userId
                KeyType: HASH
              - AttributeName: createdAt
                KeyType: RANGE
            Projection:
              ProjectionType: ALL

My delete todo lambda function looks like this:

export const handler: APIGatewayProxyHandler = async (
  event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
  const todoId = event.pathParameters.todoId

  logger.info('Event: ', event)
  logger.info('Deleting a todo with id: ', todoId)
  // TODO: Remove a TODO item by id
  await deleteTodo(todoId, getUserId(event))

  return {
    statusCode: 204,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Credentials': true
    },
    body: null
  }
}

And I am deleting an item like this:

async deleteTodo(todoId: string, userId: string): Promise<any> {
    await this.docClient
      .delete({
        TableName: this.todosTable,
        Key: {
          userId,
          todoId
        }
      })
      .promise()
  }

But, when I am try to delete an item I get:

502 Bad Gateway

In cloudwatch logs I can see that I get this error:

2020-10-12T08:43:45.648Z 7ff99035-bf3d-4158-b311-1e6d7e73a6e5 ERROR Invoke Error {"errorType":"ValidationException","errorMessage":"The provided key element does not match the schema","code":"ValidationException","message":"The provided key element does not match the schema","time":"2020-10-12T08:43:45.629Z","requestId":"SRSL15PC9C3BN4RC4F5K3ERFNNVV4KQNSO5AEMVJF66Q9ASUAAJG","statusCode":400,"retryable":false,"retryDelay":49.16692204166978,"stack":["ValidationException: The provided key element does not match the schema"," at constructor.httpResponse (/var/task/src/lambda/http/webpack:/node_modules/aws-sdk/lib/protocol/json.js:47:1)"," at constructor.shift [as callListeners] (/var/task/src/lambda/http/webpack:/node_modules/aws-sdk/lib/sequential_executor.js:100:1)"," at constructor.doneCallback [as emit] (/var/task/src/lambda/http/webpack:/node_modules/aws-sdk/lib/sequential_executor.js:75:1)"," at constructor.call [as emitEvent] (/var/task/src/lambda/http/webpack:/node_modules/aws-sdk/lib/request.js:683:1)"," at constructor.emit (/var/task/src/lambda/http/webpack:/node_modules/aws-sdk/lib/request.js:22:1)"," at r.call [as runTo] (/var/task/src/lambda/http/webpack:/node_modules/aws-sdk/lib/state_machine.js:14:1)"," at runTo (/var/task/src/lambda/http/webpack:/node_modules/aws-sdk/lib/state_machine.js:26:1)"," at constructor.done (/var/task/src/lambda/http/webpack:/node_modules/aws-sdk/lib/request.js:38:1)"," at constructor.call (/var/task/src/lambda/http/webpack:/node_modules/aws-sdk/lib/request.js:685:1)"," at constructor.Error [as callListeners] (/var/task/src/lambda/http/webpack:/node_modules/aws-sdk/lib/sequential_executor.js:108:1)"]}

I am suspecting that I have set up hash and range keys for the dynamodb table wrongly. I am not sure what is the correct way to set this up for such table?

1

1 Answers

2
votes

In your table definition, the key consists of todoId and createdAt. Your delete function however uses todoId along with userId as key. This doesn’t work.

I’m not sure why you need the user ID when you already have the todo ID which I would assume to be unique. Does it work if you simply remove the userId from the Key attribute in the delete parameter object like this?

async deleteTodo(todoId: string, userId: string): Promise<any> {
    await this.docClient
      .delete({
        TableName: this.todosTable,
        Key: {
          todoId
        }
      })
      .promise()
  }