1

This is my current lambda function, which return all data from Table "Banner".

getBanner.js

'use strict'
const AWS = require('aws-sdk');

exports.handler = async function (event, context, callback) {
    const documentClient = new AWS.DynamoDB.DocumentClient();

    let responseBody = "";
    let statusCode = 0;

    const params = {
        TableName : "Banner",
    };

    try{
        const data = await documentClient.scan(params).promise();
        responseBody = JSON.stringify(data.Items);
        statusCode = 200
    }catch(err){
        responseBody = `Unable to get products: ${err}`;
        statusCode = 403
    }

    const response = {
        statusCode: statusCode,
        headers:{
            "Content-Type": "application/json",
            'Access-Control-Allow-Origin': '*', // Required for CORS support to work
        },
        body: responseBody
    }

    return response
}

It is the sample returned data

[
   {
       Order: 123,
       Year: 2000
       ...
   },
   {
       Order: 77,
       Year: 2007
       ...
   }
]

I understand If I want to do some query for attribute order, I need to change to following.

...
var params = {
    TableName : "Banner",
    KeyConditionExpression: "#od = :yyyy",
    ExpressionAttributeNames:{
        "#od": "order"
    },
    ExpressionAttributeValues: {
        ":yyyy": 1
    }
};
...

What if I want to do the following?
enter image description here There may be some use cases which make me need to do query for another attribute, or multiple attributes.

How should I do have a function which can handle the queryStringParameters dynamically?

Updated based on the comment, but another error occurs

'use strict'
const AWS = require('aws-sdk');

exports.handler = async function (event, context, callback) {
    const documentClient = new AWS.DynamoDB.DocumentClient();

    let responseBody = "";
    let statusCode = 0;

    console.log(event)
    const queryStringParams = event.queryStringParameters;

    console.log(queryStringParams)

    let KeyConditionExpression = ''
    let ExpressionAttributeNames={};
    let ExpressionAttributeValues = {};
    for (const property in queryStringParams) {
      KeyConditionExpression += ` #${property} = :${property} ,`;
      ExpressionAttributeNames['#'+property] = property ;
      ExpressionAttributeValues[':'+property]=queryStringParams[property];
    }

    KeyConditionExpression= KeyConditionExpression.slice(0, -1);

    const params = {
        TableName : "Banner",
        KeyConditionExpression: KeyConditionExpression,
        ExpressionAttributeNames: ExpressionAttributeNames,
        ExpressionAttributeValues: ExpressionAttributeValues
    };

    console.log(params)   //Response A

    try{
        const data = await documentClient.scan(params).promise();
        responseBody = JSON.stringify(data.Items);
        statusCode = 200
    }catch(err){
        responseBody = `Unabel to get products: ${err}`;
        statusCode = 403
    }

    const response = {
        statusCode: statusCode,
        headers:{
            "Content-Type": "application/json",
            'Access-Control-Allow-Origin': '*', // Required for CORS support to work
        },
        body: responseBody
    }

    return response
}
Unabel to get products: ValidationException: ExpressionAttributeNames can only be specified when using expressions

This is the value of the ‘params’ obtained from cloudwatch Response A

{
  TableName: 'Banner',
  KeyConditionExpression: ' #order = :order , #year = :year ',
  ExpressionAttributeNames: { '#order': 'order', '#year': 'year' },
  ExpressionAttributeValues: { ':order': '1', ':year': '2007' }
}

3 Answers 3

2

Use FilterExpression when using scan instead of KeyConditionExpression. Sample

const queryStringParams = event.queryStringParameters;
  let FilterExpression = "";
  let ExpressionAttributeNames = {};
  let ExpressionAttributeValues = {};
  for (const property in queryStringParams) {
    FilterExpression += ` #${property} = :${property} ,`;
    ExpressionAttributeNames["#" + property] = property;
    ExpressionAttributeValues[":" + property] = queryStringParams[property];
  }

  FilterExpression = FilterExpression.slice(0, -1);
  

  const params = {
    TableName: tableName,
    FilterExpression: FilterExpression,
    ExpressionAttributeNames: ExpressionAttributeNames,
    ExpressionAttributeValues: ExpressionAttributeValues
  };
Sign up to request clarification or add additional context in comments.

Comments

1

Simple example

const getParams = (key, value) => ({
  TableName: "Banner",
  KeyConditionExpression: "#key = :value",
  ExpressionAttributeNames: {
    "#key": key
  },
  ExpressionAttributeValues: {
    ":value": value
  }
});

And call this function based on your query parameters. Query parameters you can get from the event.

const queryParams = event.queryStringParameters;

2 Comments

What if I have multiple queryParams?such as (year, order , name)
You should have an index in the dynamoDB and use the KeyConditionExpression for it. For the multiple params, you should use FilterExpression. The KeyConditionExpression works only with the key in from the index primary key and sort key.
1

You can this query parameters from the event object in your Lambda function handler. The event object content depends on what service invoked the Lambda function. If Lambda function invoked from Elastic Load Balancing or from API gateway you can get the query parameters from the event object. The event object will have an attribute called queryStringParameters which have all the query string parameters in the request.

This is a sample for an event object from the documentation:

{
    "requestContext": {
        "elb": {
            "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a"
        }
    },
    "httpMethod": "GET",
    "path": "/lambda",
    "queryStringParameters": {
        "query": "1234ABCD"
    },
    "headers": {
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "accept-encoding": "gzip",
        "accept-language": "en-US,en;q=0.9",
        "connection": "keep-alive",
        "host": "lambda-alb-123578498.us-east-2.elb.amazonaws.com",
        "upgrade-insecure-requests": "1",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
        "x-amzn-trace-id": "Root=1-5c536348-3d683b8b04734faae651f476",
        "x-forwarded-for": "72.12.164.125",
        "x-forwarded-port": "80",
        "x-forwarded-proto": "http",
        "x-imforwards": "20"
    },
    "body": "",
    "isBase64Encoded": false
}

https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html https://docs.aws.amazon.com/lambda/latest/dg/lambda-services.html

Example for your case: (NOTE: name of the query strings must be the same as expression attributes)

const queryStringParams = event.queryStringParams;
const updatedQueryStringParams = {};

Object.keys(queryStringParams).forEach(key => updatedQueryStringParams[`:${key}`] = queryStringParams[key]);

    var params = {
        TableName : "Banner",
        KeyConditionExpression: "#od = :year",
        ExpressionAttributeNames:{
            "#od": "order"
        },
        ExpressionAttributeValues: {
            ...updatedQueryStringParams
        }
    };

6 Comments

Can you give some example code to show how to use queryStringParameters dynamically?
const { order, year } = event.queryStringParameters; var params = { TableName : "Banner", KeyConditionExpression: "#od = :yyyy", ExpressionAttributeNames:{ "#od": "order" }, ExpressionAttributeValues: { ":yyyy": year } };
Is there dynamic way to do it? Because i may use more or less attributes for querying
Yes, you can for loop keys in your event.queryStringParameters and add to each key colon before it's name, and then user spread operator to assign key and values to ExpressionAttributeValues. But this comes with a security price. I will provide example for you in the answer above.
Thanks. But why this comes with a security price?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.