Prisma Meetup #7 - Ryan Dsouza - Deploying Prisma on Lambda using the CDK

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
(cheerful music) - Our next speaker is going to be Ryan Dsouza. And Ryan Dsouza is gonna be speaking about using Prisma together with Lambda, AWS Lambda and how to deploy Prisma on Lambda using the CDK. So you will see how this tool, the CDK, which allows you to essentially create resources in AWS, you can use that to deploy. And in case you're not familiar with Ryan, he's a Developer Success Engineer who loves working with TypeScript and orchestrating applications on the cloud. You may have seen some of his other talks and videos on YouTube where he's showing a lot of the fun stuff happening on AWS. So now without any further delay, I bring you Ryan D. Ryan, how's it going? - Daniel, is going great. How about you? - Doing well. Just got back from holiday. So full of good energy and... Yeah, excited for your talk. - Awesome. Yeah. And it's Prisma Meetup with Prisma t-shirts so yeah, it goes great. - Oh yeah. I see you're wearing the confetti shirt from the Prisma Day limited edition. (Ryan laughs) Representing. Wonderful. Also a quick reminder to the audience. We have a raffle running. I believe that you're familiar with this book, Ryan, "Designing Data-Intensive Applications" by Martin Kleppmann. We're giving this away. You can go to the raffle, I will drop a link in the comments. And now that you've shared your screen, Ryan, are you ready to go? - Yup. I'm ready to go. - Okay. The stage is yours. - Awesome. So today we will be looking at deploying Prisma on Lambda. Now Prisma can be deployed on Lambda with various tools like the serverless framework. And you can also deploy it with native CloudFormation on AWS, but today we will be looking at using the CDK for deploying Prisma. How is it different and what advantage does it provide? So you can see that we will be using CDK with TypeScript. And you will get full type-safety for your AWS resources as well as type-safety from Prisma for your database models. Right. So I am Ryan, a Developer Success Engineer at Prisma and Daniel gave a great introduction. So I'll just be saying that whoever is in our Prisma Slack channel would know me as I've interacted with most of them, so good to see you on this talk. Moving on to the topics for today. So first we will see why serverless?. Like why would we choose serverless over traditional servers? Then we will move on to a basic introduction to Prisma. Then finally, we have a code walkthrough where we will deploy the application. Like we will have our CDK resources and we will deploy it along with our Lambda function, with Prisma bundle. We will also be running a load test using a tool, a CLI tool called Artillery. Now what this does, this will run a load test against our API and we'll see that how much time does it take for the API to respond. And the last point will be some takeaways for this talk. Like what improvements can we make? And what are the changes that can be done in this after deploying and after testing this? All right. So let's move on to "Why serverless". There are some points where we have differences between traditional servers and serverless. And the very first that comes to mind is a way to run your code without manually provisioning servers. What we used to do is that we used to have EC2 and DigitalOcean to create Droplets. And we used to specify the size of the Droplet, we used to specify how much, what is the RAM, what is the storage space. But in serverless we do not need to do anything of that sort, we just upload the code that we have and the serverless platform in this case, AWS, will launch the environment automatically. So the Lambdas will be done automatically. The environment will be provisioned. We do not do anything else, just run our code. So this brings us to the next point, is easier deployment. We are just shipping code and not managing servers, so it's easy to scale. If I have 1,000 invocations, if I have 10,000 invocations of requests coming in, Lambda will scale as per that, we just have to set the amount of concurrent invocations that we need and Lambda will automatically scale based on that, we do not need to provide anything else. It is also secure by default. Now, usually when you have long-running instances, you "ssh" into them and then you deploy them. They can also be accessed remotely. But using Lambda, AWS provisions the environment for you and just runs your code. You cannot access that environment, you cannot "ssh" into that. And you cannot add or delete anything from that environment, which makes it super secure by default, rather than just having a separate instance or even deploying your server on-premise. So these are the reasons that why we would choose serverless over a traditional based server applications. And now let's see how Prisma comes into play. Prisma, if you don't know, is an ORM for interacting with your database. You create models in Prisma using a schema language called the Prisma schema language that will be mapped to the specific database tables that you have for your given database. In this walkthrough we will be using Postgres as our database. The best part is you can get fields exactly as required from the database because we have built-in type-safety from Prisma. So whenever you generate the PrismaClient, it generates types based on your database fields. So whatever you have defined in your model, you will get types for that and whenever you want to fetch something, like if I want to fetch the name and the age of all the users in my database, I just have to type the name and it will automatically be added using auto-complete and IntelliSense, thanks to TypeScript. Also you can perform migrations. So Prisma Migrate is a CLI where you can perform imperative migrations, which means that if I have to make a change, if I have to add a column, then I can simply add that and I can also add some specific changes required because Prisma Migrate generates SQL migration. So I can change that directly in SQL and I can get the specified output that I need. Also Prisma has an easy-to-interact UI, known as Prisma Studio, where I can fetch the data where you can see the entire models and tables that you have. We will be looking at that as well in the walkthrough. Moving on to defining the schema. So now we have our User model and our Post model in this case. So there are two models defined, and this will be mapped to the specific tables in the database. So it will be a User table and a Post table. We have defined fields like "id", "name", "posts", and we have some specific attributes specific to Prisma. So this is the Prisma schema language. And we have attributes like "@default", "@unique", and "@updatedAt", which will map to the specific values in the database. So I can say that my "id" is autoincrementing, so I do not need to specify that. It will be automatically added to the database. I will specify that my email is unique. So this will create a "@unique" constraint on my "email", so no two users can have the same email. There is also the "name", and the "name" is optional because we have had a question mark here, which means that this is an optional field. And then we have some other fields. Moving on to the Post model. We have the same number of fields and we have these data types mapped to the fields. So I have a string and I also have a boolean. Now these will be mapped to the specific types of your database. So if you're using MySQL, Postgres or even MongoDB, we support MongoDB now. So these will be mapped to those specific types of your database. Finally, we have something known as a relation. So in Prisma, relations are defined using the "@relation" attribute, where we specify the model name. So a Post can have an author. So the "author" is specified using the User model and we specify the "@relation" attribute, where we say that this is mapped with the "authorId". So basically if you have worked with databases, you know that we have a concept called foreign keys. So basically the "authorId" is a foreign key that references the user ID that is present in the User model. So this is how we can map our relations using Prisma. And as you can see here that the User model has "posts". And this "post" is an array, which means that a User can have many Posts. So you can think of it as a mapping in your JavaScript or TypeScript data types where you have multiple things of the same model. So a User can have multiple Posts. And the Post is mapped to a specific author, which is the user in this case. So this is an example basically of a one-to-many relation. Yeah. So this was the schema. Now, moving on to the demo. Let me show you the code. And here we have the entire code. This has also been deployed to GitHub, so at the end, Daniel is going to share the link with you. Basically, this is our "package.json". This is our CDK application in which we will be using Prisma. And we have some dependencies here. These are specific to AWS, so we will be deploying AWS resources. And what we will be deploying is an API. So we will be using "aws-apigateway". EC2 is for deploying a VPC. So we will be deploying an RDS database and for that we need a VPC. This one's interesting, "aws-lambda-nodejs". This is what will help us deploy our Prisma Lambda function. And we will be seeing how that we can bundle Prisma along with our Lambda. And this uses a tool called "esbuild" under the hood. "esbuild" is a bundler and which performs all the necessary tree shaking and bundles our application. Then we have AWS RDS. RDS will be creating our database. So in this case, we will be using Postgres as our database. I will be showing the stack that is going to be created. Finally, we have "@prisma/client". And we have the Prisma CLI in devDependencies. This is the basic setup. Let's move on to the CDK part. The CDK project is defined using a file called "cdk.json", where we have all the necessary variables defined to deploy our applications. So think of this as CDK's feature flags, where we specify a feature that we want, and whenever we run a command called "cdk deploy", these features will be taken into consideration and our stack will be deployed accordingly. What we have here is "ts-node" because we will be first building our application. We are writing our CDK stack in TypeScript, so we need "ts-node" to build our application and then it will run and deploy it. Moving on to our schema. So the schema is quite small because for the sake of this demo, I just created a sample schema. What I did was provide a data source here with the PostgreSQL as the "provider" and our database URL. The database URL will be similar to what we have in the "example.env". So I have a PostgreSQL string with the password, Postgres host and the port and our database name. I will be also adding a "connection_limit=1" so as to limit the connections for our Lambda function, because it's possible that the Lambda will be reused for subsequent invocations, so the Lambda container uses the same connection. Moving on to the generator. We have a client generator in which PrismaClient is our provider, which will be generating the types. Now the main part to note here is the "binaryTargets". What we specify here is that Prisma uses something known as a query engine. This query engine is something that Prisma uses to connect with the database and to perform all your calls and return the data specified. So this is the main part. Now this query engine is operating system-specific. So if you're running Windows, you will have a Windows query engine, if you are running Mac, you will have a Mac-based query engine. And we are running on AWS Lambda, which uses an operating system called Amazon Linux 2. And the "binaryTarget" we need to specify for that is "rhel-openssl". So Prisma documentation has a list of binary targets that can be specified, and for this we need to use "rhel-openssl". And we will see that how we bundle this binary into our final Lambda. Finally, we have the User model and we will just create this model as of now with two fields, so the "id" and the "name". Now for sake of simplicity, I have already created the models and pushed it to the database. So I've already deployed the application, but I will show you the steps to do that. And I also seeded this database with some data. So let's look at what the seed contains. So I'll open Studio. And Studio is a nice UI as I mentioned, that will fetch our models, and it'll fetch all the rows from our data. And here is the data in our table. We have a lot of rows here. So this is the "id" and the "name". I have pre-populated this. And I think we have around, yeah, we have around 100 rows. This is the data that we have. And we will be fetching this data and performing a simple load test and checking that how responsive this is on Lambda. Finally, let's move on to the stack. Now, this is the important part. Like this is where we will be setting our Lambda function and building our Prisma into the Lambda. These are the basic inputs. Like we have all our imports for "aws-apigateway", Node.js, EC2, RDS. The first thing we do is that we create a VPC and RDS database. So let me quickly walk you through that. This is a simple VPC. We have created a VPC with two availability zones. So one public and one private. So we have a public and private sub-net group. And then we create a security group. Now this is what will allow RDS to be communicated over the internet. So now our Lambda will be deployed and our Lambda needs to connect to RDS for it to function. So what we do is that we open the port 5432. This is the port for Postgres, as we use normally. And we open RDS to the world. Like we say that Lambda will be accessing this. So for this example, I've used a simple opening of a port, but in more secure cases, you will have a different setup with stricter rules. Moving on to the RDS database. This is a simple RDS database. So the best part of CDK is that you get to use TypeScript to create your entire stack. So I'm using Postgres version 12. So this is the Postgres version. And you can see here the version supported by AWS easily. So these are all the versions supported. And we were using Postgres version 12. We will be using a small "instanceType" of T2 micro. So this will be our RDS instance, a small one. Usually RDS instances of Postgres servers have a limited connection limit that you can have, which is why we will be using a small database in this case, just for testing. The rest is the same. We will be creating our database, Prisma. We will be mapping our security group. This is important because we need to specify that Lambda has access to RDS. We also make it publicly accessible at the moment, so that it can be easily accessed. We can easily run our API against it. So this was our VPC and our RDS database. Let's move on to the meat of the CDK resources, that is our Lambda function. This is our Lambda function using the "Nodejsfunction" class. So we have created an instance of this function. Now what this function needs is parameters from AWS Lambda. So we've passed that we are going to use "NODE_JS_14.x". This is our entry point. I will be showing you the entry point later. But this is the entry point to our function in which we will be initializing Prisma, and we will be calling the Prisma API and returning the response. This is the "timeout". I used a five-second default timeout and a "memorySize" of 512 megabytes. This should be enough for a basic application, and then you can increase it as your demands rise. Then we have the "environment". The "environment" is what environment variables will be passed to Lambda. Now, as you must have interacted with Prisma, you know that we usually pass the database URL using an environment variable. So what we have specified in our schema is that "environment" name would be "DB_URL". And that's the same thing we will be passing here. And this is coming from the ".env" file that we have here. This ".env" file is- that contains our entire RDS connection string, which I will be showing you. And this is mapped to our Lambda. Then we go on to the important part of the bundling process, where we will be bundling Prisma into our Lambda function. So what we do is that we say that we have two Node modules, "@prisma/client" and "prisma". And they are external modules, which will be used by our Lambda function. Now this Lambda function constructor provides us with specific command hooks. Now these are hooks that will be used while our Lambda function is being built. So think of it as performing any operation before the build is done or after the build is done. Basically pre- and post-type of hooks. So what we are interested in is before install and after bundling. So before installation, like before we install our Node modules and bundle our Lambda function, we want to copy the "prisma" directory to our output where our Lambda will be bundled. So what we are doing is that we are copying this entire "prisma" directory and we are copying into the output. Now, usually CDK outputs it to a directory known a "cdk.out" and this is our function which is created. So I already deployed this, so it is easy to see. This "prisma" folder will be taken from here and copied here. We need this because we will be running "prisma generate" and we'll be generating the query engine in the next step. Now let's look at the next step. The next step is going into the output directory. So we will be going into this directory specifically. We will run "yarn prisma generate". Now what "prisma generate" will do is generate our query engine. The query engine will be located here. This is our query engine. So this is the query engine. And you remember that we specified something known as "rhel-openssl". This is the same query engine. So this is what is going to be used by Lambda. And this is what is generated using "yarn prisma generate". What we need to do now is- "prisma generate", what it does is it defines all the engines, like the migration engine, the other engines for introspection, and stuff that we do not need to run our Lambda function. It also increases the bundle size, which is why it's not recommended to include that, and it's not necessary for our application to run. So what we do is just remove these. So what we do is perform a simple "rm -rf", that will remove the "node_modules/@prisma/engines" folder, and we also remove any specific Node modules that were present. So this decreases the bundle size. Basically what was included with the extra engines. We just have a final OpenSSL engine, the query engine, that will communicate with the database and then we will be removing all the extra folders that we have. So this is the entire process of how to bundle the Lambda function. That part will be enclosed in this. I can show you this by running "yarn cdk synth". What "cdk synth" does is that it creates a specific CloudFormation template and it creates a function. So we'll look at that later. And finally we have our REST API. This is a simple API Gateway REST API in which we pass our Lambda handler that we created above using the NodejsFunction. And what does handler contains is an initialization of PrismaClient. So we pass PrismaClient and we say that the URL is coming from here. We just say that it's coming from the environment. And we also have a "handler". Now, this "handler" is what will be executed when we hit our API. So our API endpoint will be created, and this is what we will be hitting. And we can see the output. So what this does is that it fetches all the users. So as I showed you in Studio, we have around 100 users. So this will fetch all our users right now. And then this will send it in the "body". We will look at the output of this, and this will be simply fetching 100 users and sending them as JSON. This is simply what our Lambda function will do. That is all there was to the stack. Now let's check the output of "yarn cdk synth". Now, this is the command. I am performing a "cdk synth". And this is the profile that I'm using. So if you are familiar with the AWS CLI, we have to set a specific profile with our access and secret key so that we have permissions to deploy the stack. So what we will do is that I would set a profile, and my profile is "prisma_demo". What this does is this performs a "cdk synth", and I'm using "dotenv" to pass the environment variables. So this will be passed to our Lambda function. And as you can see here, this is our Lambda function which is being bundled. So our PrismaClient is being installed. Prisma. And we have generated this. And as we can see here we have performed the binary target generation. So the binary target generation will be "rhel-openssl". And this is where we generate our PrismaClient, and now this is perfectly usable for AWS Lambda. We can simply deploy our stack using "cdk deploy". For the sake of time, I have already deployed the stack, but you can run the same command and deploy this entire stack. I'll just show you what this looks like. So this is the AWS console. I have the RDS database here. This is already deployed. This uses Postgres, and this is the endpoint that we have. So we will be hitting this endpoint. And usually when we deploy an RDS using CDK, it generates the username and passwords using Secrets Manager. So what do you need to do is go to Secrets Manager, go to this part and go to the secret and fetch the secret value from here. So this retrieved secret value will give you the username, password, port, everything. And that you can fetch and add it to your environment variable. Now let's look at CloudFormation and see if our stack is present there. I need to show you the API Gateway endpoint URL. So after deploying, API Gateway will generate a specific URL here. And here we have the stack. It will generate the URL in the output section. This is our API endpoint. This is what we will be performing a load test on. And let me show you a sample. So this is the API. Let me refresh it. This should give all the data. So this is all the data of 100 users that we have. This specifies, and it's returned in the form of JSON. So this is the entire process of deploying Prisma Lambda to the CDK. Let's run a sample load test using Artillery. So what I did is I prepared a short script, which will load test this against our API URL. So what this does is this performs an Artillery quick test. What this does is performs 50 users concurrently sending 500 requests to our Lambda. And this has started the phase. And we can see that we are getting 200, which is great. Lambda is working as expected. And we can see here the minimum time taken, the maximum time taken and the average, which will be around 260 milliseconds. And this will perform a load test on various scenarios based on the time. And as we can see, we are getting all 200s, which is great. So our Lambda is working perfectly and Prisma is handling the requests very well. So we are getting the requests of all 100 users concurrently very well. Let's let this run in the background for now, and move on to the final part of the slides, which is the takeaways that we have from this talk. First takeaway is that, only bundle the query engine for Lambda's operating system. So this is very important. You do not want to bundle your native operating system's query engine. That will increase the size of your Lambda, which is not good for deployment. Always initialize PrismaClient outside the Lambda handler. So basically this is a Lambda handler and always initialized PrismaClient outside of that. So as when we have subsequent Lambda requests, disconnection can be reused and it won't take a lot of time. It also will help decreasing the execution time. Then load test your application. So perform an Artillery test. Perform a test with a tool of your choice. And load test and see how much time it takes before deploying to production to check if your application is working properly or not. If your application is having issues with connections, so RDS can have some issues with connections on very high load, so you can use something like PgBouncer if required in front of your database. And that will help a lot in connection pooling. Using a Lambda Layer is quite important for faster deployments if you have multiple functions. So in our case, we just have a single function referencing Prisma. What if you have 10 functions or 15 functions? In that case, adding Prisma to each function and deploying that separately would cost a lot more. So it's better to separate Prisma into an own layer. So Lambda has a thing called Layers, which helps you separate it from your actual function, so you can deploy plasma separately as a Layer, and then you can just use specific functions and reference Prisma from there. So you can deploy 15 functions and that will be easily referenced, and it will take a lot faster to deploy. So that is another great thing. Finally, we will be introducing a data proxy for serverless in the near future. So what this will do is that, it's a complete game changer. So you will not be needing the query engine anymore for your Lambda. What Prisma will do, is Prisma's Cloud platform will have the query engine and you will be communicating from your Lambda to Prisma Cloud using an API. So if you have used something known as Aurora Serverless, you must be familiar with something known as the Data API. What this does is uses a specific HTTP API and then queries for the data using SQL. So that's what- That is similar to what Prisma's Data Proxy would be. And that will take away a lot of problems with respect to connection pooling and stuff. So this is something to watch out for in the future, and this is going to be an exciting product. Finally, there are some resources that I would like to share. First of all, the Prisma Docs, which are very helpful. They will specify everything right from the query engine you need and what is the specific connection limit you need to set. This repository with code plus slides will be specified by Daniel in the YouTube live stream. And also there's a link in this resource to an early access form. So if you are interested, you can sign up for an early access for Prisma's Data Proxy and try it out, which seems to be exciting. I, myself, am quite excited for this. And that was all from my end. Thank you very much for joining and listening to this. I hope you took something from this. And if you have any queries deploying this, or if you have any queries in specific regarding to Prisma or AWS, you can reach me on any of these platforms. I have my link specified here, and I'll stop sharing my screen now. - Ryan, thank you so much for that. That was quite an insightful talk and a really nice walkthrough of AWS and CDK. So if I understand correctly, CDK essentially allows you as a developer to define resources, basically using TypeScript code, right? - Yep, exactly. So we have TypeScript and basically we can specify our resources as well as our application code. So that will be coupled together. - I see. Well, let's see if we have any questions from the audience and if not, I'd like to- Yeah, we didn't have any questions. We had someone who was praising you while you were giving the talk. So thank you for that. And Ryan, thank you again for the talk and I hope to see you here soon. - Thank you for having me. (cheerful music)
Info
Channel: Prisma
Views: 138
Rating: 5 out of 5
Keywords: TypeScript, NodeJS, ORM, Database, Developer
Id: 9zr6LHaMMYA
Channel Id: undefined
Length: 31min 1sec (1861 seconds)
Published: Thu Sep 16 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.