Reputation: 936
Can anyone explain how to use GetItemInput
type when calling DocumentClient.get
?
If I pass in an object of any type get
works but if I try and strongly type the params object I get this error:
ValidationException: The provided key element does not match the schema
Here is my lambda function code where I pass the params as type any
:
export const get: Handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
console.log(event.pathParameters)
if (!event.pathParameters) {
throw Error("no path params")
}
const params: any = {
Key: {
id: event.pathParameters.id
},
TableName: table
}
console.log(params)
try {
const result: any = await dynamoDb.get(params).promise()
return {
body: JSON.stringify(result.Item),
statusCode: result.$response.httpResponse.statusCode
}
} catch (error) {
console.log(error)
return {
body: JSON.stringify({
message: `Failed to get project with id: ${event.pathParameters!.id}`
}),
statusCode: 500
}
}
}
And here is my attempt to get it to work with type GetItemInput
export const get: Handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
console.log(event.pathParameters)
if (!event.pathParameters) {
throw Error("no path params")
}
const params: GetItemInput = {
Key: {
"id": { S: event.pathParameters.id }
},
TableName: table
}
console.log(params)
try {
const result: any = await dynamoDb.get(params).promise()
return {
body: JSON.stringify(result.Item),
statusCode: result.$response.httpResponse.statusCode
}
} catch (error) {
console.log(error)
return {
body: JSON.stringify({
message: `Failed to get project with id: ${event.pathParameters!.id}`
}),
statusCode: 500
}
}
}
If I leave the Key
as before ala:
const params: GetItemInput = {
Key: {
id: event.pathParameters.id
},
TableName: table
}
Unsurprisingly I get a type error. But can't fathom how I can form my Key
such that I dont get the ValidationException
.
Note the id
field is of type String
in the DynamoDB
.
Upvotes: 13
Views: 30492
Reputation: 1
I'm a little late to the party but if anyone is looking for an easier way to interact with DynamoDB in Typescript, I would recommend to use some kind of ORM instead of writing all this complicated code as in the original question 😅
There are various ORMs, but the one I would recommend for Typescript is Dynamode - however I'm a bit biased as I authored it!
Here is a example how easy it is to use:
const response = await DynamoDB.getItem({
TableName: 'users',
Key: {
PK: { S: 'blazej' },
SK: { S: 'nwjła7pa31e2' },
},
ProjectionExpression: 'username, #object'
ExpressionAttributeNames: { '#object': 'object' },
});
const user = response?.Item; // can be undefined
const user = await UserManager.get({
PK: 'blazej',
SK: 'nwjła7pa31e2',
}, { attributes: ['object', 'username'] });
Other popular alternatives: Dynamoose or DynamoDB Toolbox
Upvotes: 0
Reputation: 610
For anyone like me whos too lazy to make their own dynamoDB client and wants to copy and paste one here's some code I wrote:
import { AttributeValue, DynamoDB, GetItemInput, PutItemCommandOutput, PutItemInput, QueryCommandInput } from "@aws-sdk/client-dynamodb";
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
export default class DynamoDbClient {
dynamoDB: DynamoDB;
tableName: string;
constructor(tableName: string) {
this.dynamoDB = new DynamoDB({});
this.tableName = tableName
}
async getItem<T>(inputKeys: DynamoDbClientGetItemInput): Promise<T | UnmarshalledAny> {
const params: GetItemInput = {
TableName: this.tableName,
Key: marshall(inputKeys)
};
const result = await this.dynamoDB.getItem(params);
if (!result || !result.Item) {
return null
}
return unmarshall(result.Item)
}
async query<T>(input: Partial<QueryCommandInput>): Promise<T | UnmarshalledAny> {
const params: QueryCommandInput = {
TableName: this.tableName,
...input
};
const result = await this.dynamoDB.query(params);
if (!result.Items) {
return []
}
return this.unmarshallList(result.Items)
}
unmarshallList(items: MarshalledItem[]) {
const unmarshalledItems = []
for (let index = 0; index < items.length; index++) {
const item = items[index];
unmarshalledItems.push(unmarshall(item))
}
return unmarshalledItems
}
async putItem(inputItem: DynamoDbClientPutItemInput): Promise<PutItemCommandOutput> {
const params: PutItemInput = {
TableName: this.tableName,
Item: marshall(inputItem, { removeUndefinedValues: true })
};
return await this.dynamoDB.putItem(params);
}
}
export interface DynamoDbClientGetItemInput { id: string, pk: string }
export interface DynamoDbClientPutItemInput { id: string, pk: string, data: any }
export interface UnmarshalledAny {
[key: string]: any;
}
export interface MarshalledItem {
[key: string]: AttributeValue;
}
You can install the libs with:
npm i @aws-sdk/client-dynamodb @aws-sdk/util-dynamodb
You'll need to change your key names to whatever is in your dynamoDB, mine are called pk
and id
Upvotes: 2
Reputation: 906
@ttulka's answer is perfect, but just to add on that I had the same issue and it really helped to spend 5 minutes to disambiguate the now MANY different ways of accessing DynamoDB from official AWS JS SDKs.
Reading this for 5 minutes is my answer, and it will all become clear to you after this;
https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/modules/_aws_sdk_lib_dynamodb.html
The tldr; is;
import { DynamoDB } from "@aws-sdk/client-dynamodb"; // ES6 import
import { DynamoDBDocument, PutCommandInput } from "@aws-sdk/lib-dynamodb"; // ES6 import
Upvotes: 8
Reputation: 10892
I think you mix two different client definition files DynamoDB
and DynamoDB.DocumentClient
. While you're using the DynamoDB.DocumentClient
client, at the same time you're using the interface DynamoDB.Types.GetItemInput
from DynamoDB
.
You should use DynamoDB.DocumentClient.GetItemInput
:
import {DynamoDB} from 'aws-sdk';
const dynamo = new DynamoDB.DocumentClient({apiVersion: '2012-08-10'});
...
const params: DynamoDB.DocumentClient.GetItemInput = {
TableName: table,
Key: {
id: event.pathParameters.id
}
};
const result = await this.dynamo.get(params).promise();
Upvotes: 37