I need to get multile items from dynamodb using lambda with node.js. I am using batchGet method of AWS.DynamoDB.DocumentClient(). I get below error.
{
"errorMessage": "The provided key element does not match the schema",
"errorType": "ValidationException",
"stackTrace": [
"Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:48:27)",
"Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:105:20)",
"Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:77:10)",
"Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:683:14)",
"Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)",
"AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)",
"/var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10",
"Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9)",
"Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:685:12)",
"Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:115:18)"
]
}
Below is my code .
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient();
const params = {
RequestItems: {
"usersTable_Test": {
Keys: [{
userId: '123'}
],
ProjectionExpression: "userId"
}
}
};
const res = await docClient.batchGet(params).promise();
I have also tried batchGetItem method but then I get below error.
{
"errorMessage": "docClient.batchGetItem is not a function",
"errorType": "TypeError",
"stackTrace": [
"exports.handler (/var/task/index.js:62:30)",
"<anonymous>",
"process._tickDomainCallback (internal/process/next_tick.js:228:7)"
]
}
Since you are using a sort key, you will have to include that in your parameters as well. If you were to create the database without setting up a sort key, you would only need the primary key. The ProjectExpression is also not needed in this case.
Documentation for batchGet(): https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#batchGet-property
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient();
const params = {
RequestItems: {
"usersTable_Test": {
Keys: [
{
userId: '123',
refId: 'theRefId'
}
]
}
}
};
const res = await docClient.batchGet(params).promise();
If you want to get data by only the primary key, you can use the query() method.
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#query-property
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient();
var params = {
TableName: 'usersTable_Test',
KeyConditionExpression: 'userId = :i',
ExpressionAttributeValues: {
':i': '123'
}
};
docClient.query(params).promise();
Related
I use nextjs with elastic search cloud, also use amplify and lambda created from amplify rest api.
This is my function:
export async function getServerSideProps(context) {
let esItems;
try {
const { query } = context.query;
const apiName = 'APINAME';
const path = '/searchlist';
const myInit = {
response: true,
queryStringParameters: { query: query }
};
const response = await API.get(apiName, path, myInit);
esItems = response;
} catch (err) {
console.log(err)
}
return {
props: {
allProducts: esItems ? esItems.data.items : [],
}
};
}
I return 50 products from elastic and get this error:
502 ERROR
The request could not be satisfied.
The Lambda function returned invalid JSON: The JSON output is not parsable. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
This is lambda function:
app.get('/searchlist', async (req, res) => {
const { query } = req.query;
const client = new Client({
node: "https://elastic-cloud....",
auth: {
username: process.env.USERNAME,
password: process.env.PASSWORD
}
});
const searchQuery = {
query: {
multi_match: {
query: query,
type: "phrase",
fields: [
'manufacturerTypeDescription^8',
'manufacturerName^6',
'ean^4',
'_id^2',
]
}
}
}
const typeSearch = {
index: "product",
size: 50,
body: searchQuery
}
const r = await client.search(typeSearch);
const hits = r.body.hits;
const items = hits.hits.map((hit) => ({
_id: hit._id,
...hit._source,
}))
res.json({
success: 'get call succeed!',
items
});
});
I have two DynamoDB tables: Order and Client.
When an Order is created, a Lambda function is triggered and first seeks to read the Client associated with the Order. However, the Lambda function fails to do so and does not report any error.
The Lambda function is:
var AWS = require('aws-sdk');
AWS.config.update({region: process.env.REGION})
docClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event) => {
await event.Records.forEach( async (record) => {
const { eventName, dynamodb } = record
if(eventName==="INSERT") {
console.log('A new order has been created')
const client_id = dynamodb.NewImage.client_id.S
let params = {
TableName: process.env.API_REALAPI_CLIENTTABLE_NAME,
Key: {
"id": client_id
}
}
console.log(params)
try {
const result = await docClient.get(params).promise()
console.log(result)
} catch(err) { console.log(err); throw new Error(err); }
}
})
}
When I run this, it gets to console.log(params) but then doesn't show any result nor any error.
My function was configured with the amplify cli and it appears to have read permissions to the Client table:
"Statement": [
{
"Action": [
"dynamodb:Put*",
"dynamodb:Create*",
"dynamodb:BatchWriteItem",
"dynamodb:Get*",
"dynamodb:BatchGetItem",
"dynamodb:List*",
"dynamodb:Describe*",
"dynamodb:Scan",
"dynamodb:Query",
"dynamodb:Update*",
"dynamodb:RestoreTable*",
"dynamodb:Delete*"
],
"Resource": [
"arn:aws:dynamodb:us-east-1:123456123456:table/Client-abcdefg-dev",
"arn:aws:dynamodb:us-east-1:123456123456:table/Client-abcdefg-dev/index/*"
],
"Effect": "Allow"
},
{
Am I missing something?
Thanks!
The issue is that you are using await in a forEach loop which doesn't work. Your handler is existing before the Db call is done which is why there is no response. You will want to do something more like this:
exports.handler = async (event) => {
const jobs = event.Records.map((record) => {
const { eventName, dynamodb } = record
if (eventName === 'INSERT') {
console.log('A new order has been created')
const client_id = dynamodb.NewImage.client_id.S
let params = {
TableName: process.env.API_REALAPI_CLIENTTABLE_NAME,
Key: {
id: client_id
}
}
console.log(params)
return docClient.get(params).promise()
}
})
try {
const results = await Promise.all(jobs)
} catch (err) {
console.log(err)
throw new Error(err)
}
}
I am getting the error stack below, The code looks fine to me, don't know where is the error!
My handler code is below :
import AWS from 'aws-sdk';
import commonMiddleware from '../lib/commonMiddleware';
import createError from 'http-errors';
const dynamodb = new AWS.DynamoDB.DocumentClient();
async function placeBid(event, context) {
const { id } = event.pathParameters;
const { amount } = event.body;
console.log('**********************************');
console.log(process.env.AUCTIONS_TABLE_NAME);
console.log('**********************************');
const params = {
TableName: process.env.AUCTIONS_TABLE_NAME,
Key: {
id: { S: id}
},
UpdateExpression: "set highestBid.amount = :amount",
ExpressionAttributeValues: {
":amount": { S: amount },
},
ReturnValues: "UPDATED_NEW"
};
console.log('**********************************');
console.log(JSON.stringify(params));
console.log('**********************************');
let updatedAuction;
try {
const result = await dynamodb.update({params}).promise();
console.log({result});
updatedAuction = result.Attributes;
} catch(e) {
console.log({'error': e});
throw new createError.InternalServerError(e);
}
return {
statusCode: 200,
body: JSON.stringify(updatedAuction)
};
}
export const handler = commonMiddleware(placeBid);
2021-06-16T15:41:20.179Z 722a3b28-9f67-44eb-8e5a-85368325dae5 INFO {
error: MultipleValidationErrors: There were 2 validation errors:
* MissingRequiredParameter: Missing required key 'TableName' in params
* MissingRequiredParameter: Missing required key 'Key' in params
at ParamValidator.validate (/var/runtime/node_modules/aws-sdk/lib/param_validator.js:40:28)
at Request.VALIDATE_PARAMETERS (/var/runtime/node_modules/aws-sdk/lib/event_listeners.js:132:42)
at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at callNextListener (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:96:12)
at /var/runtime/node_modules/aws-sdk/lib/event_listeners.js:86:9
at finish (/var/runtime/node_modules/aws-sdk/lib/config.js:386:7)
at /var/runtime/node_modules/aws-sdk/lib/config.js:404:9
at EnvironmentCredentials.get (/var/runtime/node_modules/aws-sdk/lib/credentials.js:127:7)
at getAsyncCredentials (/var/runtime/node_modules/aws-sdk/lib/config.js:398:24)
at Config.getCredentials (/var/runtime/node_modules/aws-sdk/lib/config.js:418:9) {
code: 'MultipleValidationErrors',
errors: [ [Error], [Error] ],
time: 2021-06-16T15:41:20.176Z
}
}
You've nested params in another object. This causes the method call to receive:
{ params: { TableName: 'AUCTIONS_TABLE_NAME', Key: { id: id}}}
Try replacing dynamodb.update({params}) with just dynamodb.update(params).
Also with the document client, you don't need to specify the type.
Key: {id} will be fine.
Trying to integrate serverless-local-authorizers-plugin to a system that uses AWS Lambda Authorizer. When I print the response it does contain a principalId and I return it in an object exactly like the online lambda response does. Any ideas why I am getting the error
Serverless: Warning: No principalId in response?
How it was returned:
{
"principalId":"user",
"policyDocument":{
"Version":"2012-10-17",
"Statement":[{
"Action":"execute-api:Invoke",
"Effect":"Deny",
"Resource":"arn:aws:execute-api:eu-central-1:my-AWS-ID:*"}
]},
"context":{
"platformRoles":"Guest",
"userId":"unknown"
}
}
local auth proxy function
const AWS = require('aws-sdk');
const mylocalAuthProxyFn = async (event, context) => {
const lambda = new AWS.Lambda();
const req = {
FunctionName: 'my-lambda-function-name',
InvocationType: 'RequestResponse',
Payload: JSON.stringify(event)
};
const results = await lambda.invoke(req).promise();
if (results.StatusCode === 200) {
return results.Payload;
}
throw new Error('Unauthorized');
};
module.exports = { mylocalAuthProxyFn };
Here is what I have figured out the works. After speaking to the library owenrs, the response has to be an explicit object in my case for some reason. They claim you can send the references variable of the promise but it didn't work for me. But the below does:
const AWS = require('aws-sdk');
const mylocalAuthProxyFn = async (event, context) => {
const lambda = new AWS.Lambda();
const req = {
FunctionName: 'aidonic-endpoints-dev-createAuthorization',
InvocationType: 'RequestResponse',
Payload: JSON.stringify(event)
};
const result = await lambda.invoke(req).promise();
if (result.StatusCode === 200) {
const pl = JSON.parse(result.Payload);
return {
principalId: pl.principalId,
policyDocument: {
Version: '2012-10-17',
Statement: [
{
Action: 'execute-api:Invoke',
Effect: 'Allow',
Resource: '*'
}
]
},
context: {
platformRoles: 'Verified Organization Representative,Registered User',
userId: '23e8320fcechi042389h02ijwqwd'
}
};
}
throw new Error('Unauthorized');
};
module.exports = { mylocalAuthProxyFn };
Developing entirely local, I followed the tutorial here: https://scotch.io/tutorials/super-simple-graphql-with-node#toc-creating-the-graphql-server
Hardly any modification, only to restrict the focus to a "product" table. No "User" or "Recipe" table.
It appears the only issue I have now is retrieving the data from the MySQL DB.
Of course I've Googled and compared tutorials extensively. I've been through the code quite a bit as well to the point it's getting blurry.
If I change the "return" to {"name":"test"}, the data passes through to my local GraphQL dashboard perfectly.
models/index.js
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs.readdirSync(__dirname).filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
}).forEach(file => {
const model = sequelize['import'](path.join(__dirname, file));
db[model.name] = model;
}); //console.log(db); Object.keys(db).forEach(modelName => { if (db[modelName].associate) { db[modelName].associate(db); } }); db.sequelize = sequelize; db.Sequelize = Sequelize;
module.exports = db;
'use strict';
module.exports = (sequelize, DataTypes) => {
const Product = sequelize.define('Product', {
name: DataTypes.STRING,
urlWordRef: DataTypes.STRING,
seoTitle: DataTypes.STRING,
seoDescription: DataTypes.STRING,
standardEquipment: DataTypes.STRING,
technicalSpecs: DataTypes.STRING
}, {});
Product.associate = function(models) {
// associations can be defined here
//Product.hasMany(models.RefProductOptions)
};
return Product;
};
// src/resolvers.js
const resolvers = {
Query: {
async products (root, { id }, { models }) {
return models.Products.findById(id) // *** THE LINE OF CODE WHICH FAILS ***
return {"name":"test"} // ** SUBSTITUTING THE ABOVE LINE WITH THIS - SUCCESS! ***
}
}
}
module.exports = resolvers
// src/schema.js
const { gql } = require('apollo-server')
const typeDefs = gql `
type Products {
id: Int!
name: String
urlWordRef: String
seoTitle: String
seoDescription: String
standardEquipment: String
technicalSpecs: String
}
type Query {
products(id: Int!): Products
}
`;
module.exports = typeDefs
// src/index.js
const { ApolloServer } = require('apollo-server')
const typeDefs = require('./schema')
const resolvers = require('./resolvers')
const models = require('../models')
const server = new ApolloServer({
typeDefs,
resolvers,
context: { models }
})
server.listen({port: 4000}).then(({ url }) => console.log('Server is running on localhost:4000'))
GraphQL query via the dashboard:
{
products(id: 1) {
name
}
}
I would expect that I can pass the id via the noted GRaphQL query above and get the correct table row result.
Alas, I'm ready to start coding with my forehead!
Error response in GraphQL:
{
"errors": [
{
"message": "Cannot read property 'findById' of undefined",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"products"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: Cannot read property 'findById' of undefined",
" at products (/home/bob/graphql-server/src/resolvers.js:6:26)",
" at field.resolve (/home/bob/graphql-server/node_modules/graphql-extensions/dist/index.js:140:26)",
" at resolveFieldValueOrError (/home/bob/graphql-server/node_modules/graphql/execution/execute.js:486:18)",
" at resolveField (/home/bob/graphql-server/node_modules/graphql/execution/execute.js:453:16)",
" at executeFields (/home/bob/graphql-server/node_modules/graphql/execution/execute.js:294:18)",
" at executeOperation (/home/bob/graphql-server/node_modules/graphql/execution/execute.js:238:122)",
" at executeImpl (/home/bob/graphql-server/node_modules/graphql/execution/execute.js:85:14)",
" at Object.execute (/home/bob/graphql-server/node_modules/graphql/execution/execute.js:62:35)",
" at /home/bob/graphql-server/node_modules/apollo-server-core/dist/requestPipeline.js:239:46",
" at Generator.next (<anonymous>)",
" at /home/bob/graphql-server/node_modules/apollo-server-core/dist/requestPipeline.js:7:71",
" at new Promise (<anonymous>)",
" at __awaiter (/home/bob/graphql-server/node_modules/apollo-server-core/dist/requestPipeline.js:3:12)",
" at execute (/home/bob/graphql-server/node_modules/apollo-server-core/dist/requestPipeline.js:218:20)",
" at Object.<anonymous> (/home/bob/graphql-server/node_modules/apollo-server-core/dist/requestPipeline.js:156:42)",
" at Generator.next (<anonymous>)",
" at fulfilled (/home/bob/graphql-server/node_modules/apollo-server-core/dist/requestPipeline.js:4:58)",
" at <anonymous>",
" at process._tickCallback (internal/process/next_tick.js:188:7)"
]
}
}
}
],
"data": {
"products": null
}
}