Now we're getting somewhere 🎉
Our stack has plenty of resources and how it's the time to start combining them together.
In this lesson we're going to create a new lambda function that will read data from our new TodoTable
.
Since we don't want to add unnecessary noise, we're going to define the lambda function in the TodoBackend
stack.
After all - whoever will end up using that stack shouldn't have worry too much about the underlying details. That's the power of CDK - we get to abstract away unnecessary implementation details and focusing on solving problems by deploying infrastructure and resources we need to solve them.
Instructor: [0:00] Now that we have a database with some todo items inside of it, it is time for us to build our serverless backend.
[0:05] We're going to create a brand-new Lambda function that is going to be responsible for reading, saving and deleting data from the database. I'm going to call it a todo-handler.ts. I'm also going to delete our help.ts function because this is not a thing that we are going to need anymore.
[0:20] Next, inside of the todo-handler, I'm going to paste in some code that you can find in the source code for this lesson. Let's go through it.
[0:26] First up, this handler is an asynchronous function that is going to take an event from the API Gateway. Whenever this function is going to receive a GET request, so whenever the HTTP method taken from the event is going to be equal to GET, we are going to get all the todos.
[0:39] We can see the GET all the todos function over here. Right now, it is completely empty and we're going to implement that in just a second. Once we have all the todos, we're going to use the CreateResponse() function in order to create a response for our Lambda function.
[0:51] By default, this is going to return a status code of 200. Also, it is going to return a body, so a body is going to be a stringified version of either a string or elements from a DynamoDB table.
[1:01] In order to get the data from our DynamoDB table from the Lambda function, we are going to need two things. First up, the table name. That is something that we are going to get from the environment variable and secondly, we need a programmatic way of interacting with DynamoDB from TypeScript.
[1:15] In order to do that, we are going to use the AWS DynamoDB DocumentClient. In order to learn to how to use that, we can check out this handy cheat sheet by Nader Dabit over here. We can see that the DynamoDB DocumentClient is the easiest and most preferred way to interact with a DynamoDB database from a Node.js or JavaScript application.
[1:31] If we scroll down a bit, we are going to be able to see a scan operation. A scan operation allows us to scan and return all the items in the database. We can use that in order to get all the todos.
[1:42] Here we can see that in order to create a scan operation with the DocumentClient, we need to pass in the params. The only param, and it is mandatory, is the table name and afterwards is going to return us a promise and we can get the data from the value returned from this promise.
[1:55] Let's implement that. I'm going to do const scan-result. That is going to be equal to Dynamo. This is the Dynamo DocumentClient.scan. I'm going to pass in the object as an argument. This object is going to have a field table-name which is going to be equal to "The table name." This is the one that we have defined over there.
[2:14] Next up, I will be returning a promise. Since this is going to return a promise, I need to add an await over here. I will be returning the scan-result from the function. Now we can go ahead and save that. Now the code for our Lambda function is prepared so let's go ahead and deploy it.
[2:27] In order to do that, go to our todo-backend construct and here we are going to create a new Lambda function. First import * as Lambda from '@aws-cdk/aws-lambda'. Next create a new Lambda function. I'm going to do const handler = new-Lambda.function. The first argument is going to be this. The second argument, a todo-handler. The third one, the props.
[2:50] First up, we have to pass in the code. The code is going to be Lambda.code from asset and I'm going to use the Lambda directory.
[2:57] Next up, the handler property. This is the name of the method within your code that Lambda calls to execute your function, and that is going to be equal to todo-handler.handler, because if we take a look inside of the todo-handler, we can see the file name is todo-handler.ts, and the function that is going to be exported is called .handler.
[3:14] Next up, the runtime. That's going to be set to Lambda, data runtime and Node.js 12.x. Lastly, the environment variables, because we have to pass in the name of our DynamoDB table to this Lambda function so it can interact with it.
[3:28] To do that, create a variable for this DynamoDB table. I'm going to do const todos-table = our table. Next up, the environment, I'm going to set it to table-name = todos-table.table-name.
[3:41] Note that our todo-handler Lambda Function is prepared. We need to be able to call it. In our main stack, we are creating our todo-backend and we are also creating the Lambda REST API. Instead of this Hello Lambda that we have deleted in this lesson, we are going to use the todo-backend handler.
[3:57] First up, go back to the todo-handler. Here instead of const handler, we're going to do this.handler. Next up, we have to be able to export it because right now, TypeScript is complaining that the property handler does not exist on type todo-backend.
[4:10] At the top of our todo-backend class, we are going to declare that we have a public read only property of handler, and that is of type Lambda.function. Once we save that, we can go back to our stack, up-stack and clean this up a bit.
[4:22] I'm going to be deleting this Hello Lambda function, as well as the event notification because we are not going to need that anymore. Next up, I will be deleting this import as well, so deleting the S3 notifications import. Lastly, here in the Lambda REST API, we are going to set the handler to todo-backend.handler.
[4:38] Let's recap what we have learned in this lesson. In our todo-backend, we have our brand new Lambda function that we have defined in a todo-handler.ts file. This function is responsible for getting all the todos from our DynamoDB table using the DocumentClient. Right now, in our main stack, this function has been connected to our Lambda REST API todo-backend.handler.
I don't understand the syntax of putting a .promise() at the end of an await. Why not just return the data?
I think adding the .promise() makes the resulting data variable then-able. Is that the only thing it is doing or is there more to it? Thanks.
Hey!
Sorry for not getting back to you earlier, I didn't touch my laptop this weekend (which I guess is a good thing?)
Anyway! Check out this handy cheatsheet by Nader Dabit:
https://github.com/dabit3/dynamodb-documentclient-cheat-sheet#scan---scanning-and-returning-all-of-the-items-in-the-database
Basically the purpose of putting promise()
at the end is like you said - to make it then-able. Otherwise you're not going to get the data but you'll have to provide in the callback to be executed once the response is available.
I don't understand the syntax of putting a .promise() at the end of an await. Why not just return the data?
I think adding the .promise() makes the resulting data variable then-able. Is that the only thing it is doing or is there more to it? Thanks.