Video details

Rapid and type-safe Node.js development with Prisma | Daniel Norman | nodejsday 2020

NodeJS
09.05.2021
English

For developers that embrace rapid development, working with relational databases comes with a lot of challenges. But it's possible to optimize for developer experience without compromising on reliability with Prisma – an open-source end-to-end type-safe database toolkit for TypeScript and Node.js. In this workshop, we'll introduce Prisma and learn how it enables rapid and type-safe development while addressing common problems in data-centric applications.
nodejsday will be back soon, keep in touch!
www.nodejsday.it Twitter https://twitter.com/nodejsconfit Newsletter: http://bit.ly/grusp-nl

Transcript

Oh my Hello. Here we are again. Our next speaker is Daniel Dan is the developers advocate at Prisma. He's focusing on modern database tooling for NodeJS and transcript. So he's very passionate about open source development and also Turing. And he likes bringing ideas from different disciplines to software development, which I definitely agree with him about this. And today we will talk about how price Mark can help you code quickly with Typesafe guarantees. So Hello Daniel. Thank you for being with us. How are you, Thomas? Very well, very well. Nice to be here. Nice to meet you too. So thank you again. And looking forward to your talk. See in a bit. Alright. Okay. So Hello and welcome today. I will talk about as Thomas said about Rapid and Typesafe no JS development with Prisma. Before I get started, I'd like to think Julia, and all of the organizers of no JS Day for the invitation to Speak 2020 has been a wild year, and I think transition into the online format is quite an undertaking. Nevertheless, it's an honor to have this opportunity to speak alongside some of today's speakers who are talking about some of the most exciting developments in the Node JS ecosystem. So a little bit about myself. My background is mostly as a full stack engineer for the last ten years, and during that time I've worked in different parts of the stack, including like front end, back end DevOps infrastructure, and my last gig was actually a web three stack with Aetherium. Nevertheless, during that time I've worked a lot with databases and so hopefully today I can shed a bit of light on that topic and see where Prisma fits in. So our agenda for today in the introduction, we'll discuss the problem space of working with databases in Node JS and how Prisma can help with that. Then we'll take a closer look at Prisma and see how it can actually help you concretely. And then lastly, we'll finish with a live demo. So let's get started. It's 2020 and you want to build a web app. The first thing I think you need to think about is where are you going to store your data? Most applications today are data intensive in the sense that the functionality that they offer is really based on their ability to store, manipulate, and retrieve data. And so we have this diagram here below. And this is sort of a typical web architecture for a web application. And so we could see this API technology. It could be Rest or Graph QL. And then we have this Node JS server, and that might be a long running server or even a Lambda function, as Simono has shown us, or a serverless function. I should say all of these applications rely on some kind of persistent storage, and that is typically in a database. So if we try to start exploring the landscape of options, there are a couple of options. The first and really sort of simple option is using a backend as a service, something like Firebase or AWS App sync. Of course, this is a nonexhaustive list. There's many more, and these handle a lot of the concerns for you, but they require you to learn a proprietary technology that it often comes with vendor lock in, and it's often not standardized. Then we have the option of self hosted backend as a service, something like Azura, which supports Postgres and also recently added support for My SQL. And then there's also Super Base, which is also another back end, self hosted backend as a service option. And these are really great if you're looking to purely build a graph QL API or a Rest API the moment that you decide to. And this is sort of the last option. If you decide to build something yourself, you really have to start by picking your database technology, and you might choose something like a relational or SQL database, for example, Postgres or MySQL or, you might go down the route of no SQL with MongoDB. The main point here is that no matter which one you choose, the database technology that you choose will influence almost every decision about your application. And that sort of leads me to one of the main challenges right now in the Node JS ecosystem, working with databases in Node JS is difficult. There are three categories of tools when it comes to working with relational databases, and this is sort of like an overview of the current landscape of these database access libraries, and they range from sending raw SQL strings to higher level abstractions like SQL Query Builders, which give you a programmatic API to construct these SQL queries and Orms. And to think about all of these approaches is that each of them comes with its own set of problems pitfalls. And so if you're really interested in looking at that, we've published Prisoner's Data Guide, and we've done a pretty elaborate comparison of these three levels of abstraction. Also, if you're interested in the specific pitfalls of ORNs, I suggest you look up these two terms. The first is the object impedance mismatch, and the second is the Vietnam Ous computer science. So what's today all about? Well, there's some hope Prisma makes working with databases easy, and we'll look at that just now. So Prisma what is Prisma? Prisma is an open source database tool kits, and Prisma consists of three main tools. The first one is Prisma Client, which can be used in Node JS and TypeScript project, and essentially it replaces what Orm's query builders, or a simple database driver would typically do. Then we have Prisma Studio, which serves as like a database GUI. And the nice thing about Prisma Studio, which we'll look at in the Live demo, is that it's a really modern, intuitive way to sort of visualize what's going on in terms of your data and your database. Then we have Prisma Migrate, which is used for a database migrations. So in case some of you are not so familiar with the term database migrations. Database migrations is the term that we use to refer to changing a database schema over time. And this is because when working with relational databases have you usually have to define a schema up front. And so every time you want to make a change to that database schema, you usually have to carry out like an SQL alter table command. And so a migration tool usually helps you keeping your code base all of those changes and carry them out. So prison migrate is different to many other migration stores, in the sense that it's declarative rather than imperative. So the idea is and we'll look at this in a moment is that you sort of have a declaration for how you want your database to look like an Prisma migrate figures out the changes that need to be carried out every time. So what are the goals of Prisma? It has really two main goals, and the first one is to boost productivity. I think Mate mentioned this in his talk about Graph QL. That right now developers time is one of the most expensive parts of developing applications, and so boosting productivity by letting developers query data natural and familiar ways is one of the ways that Prisma sort of pays pays service to that goal. And then, second, increase confidence. And Prisma increases confidence with type safety. Auto completion in a robust query API, which we'll look at. So how does it do that? Well, the first thing is it allows you to think in objects instead of SQL. So every Prisma client call maps to a type, and so the result is always predictable ahead of running the query. So when you actually type the you use the Prisma client API in your node JS application. You can already know what is the structure of that object that you're going to get back, which represents a record in the database. Working intuitively with relations well, fetching relations is one of the difficult parts when working with relational databases, and one of the parts where there's a lot of potential pitfalls fetching relations with Prisma client is easy because you have this fluent API, which we'll also look at in the live demo. The main thing is that you don't need to actually explicitly do joins across tables, and instead you get this intuitive programmatic API. We have the auto completion and type safety. So every database query that you type is fully Typed, and then we have this declarative and human readable database schema, which is also used for migrations, but also for the defining how you want your database schema to look like. And then lastly, this is I think a really powerful idea about Prism is that you have a single source of truth for both your database and your application models. And that is the Prisma Schema. So typically, when working with Orms or query builders. You're essentially maintaining two schemas or two models for what your data looks like. One of them is your database schema, and the other is your representation in your application. And so when you make a change to the database, typically with other tools, you need to make sure that your application models are also in sync with those changes. And it turns out that this problem is quite a burden, and it's a burden that falls on you as the developer. And so by having this single source of truth, you actually do away with that need to synchronize between the two models. So I've spoken a lot about type safety and type errors, and I've mentioned TypeScript, but this is a node JS, and it's not exclusively a TypeScript. So perhaps it's good to start with a definition of what type errors are. And so a type error is when a value in an operation is of a different type from what the code expects. And so the example that I'm showing here in one component with specific type requirements, namely the user object with first name and last name. And then we have these three different type errors that occur. And the problem with these type errors that generally when working with JavaScript, these type errors occur at runtime. And so that means that while you're writing the code, you don't have insight into these errors. And so you have to either run your tests or actually deploy the application and run the application in order to get that. I've got some good news on this. The benefits of type safety, that Prisma Office can be added zero cost in a project using JavaScript with Prisma, and hopefully I can showcase that in the live demo. And so doing a live demo, it's a slippery slope. And I suggest that we pray to the demo gods or at least try to be as calm as the Dalai Lama. So let's get to it. What are we going to do in this live demo? The first thing that I'm going to show is I'm going to show the Prisma schema and then use that Prism schema in order to create the Postgres database. Then I'm going to install and generate press my client, and then I'm going to actually write a node JS script, which will perform database operations with press my client. And then lastly, I will show how Prisma Studio helps us explore the data. So let's get to the code. So the first thing that I have open here is the project, and I'm going to open up the schema. Let's say I'm not sure why they syntax highlighting. Okay. Seems like Prisma is installed wasn't enabled. Let's see if we can get the syntax highlighting. Okay, we've got it. That's great. Okay. So here we have the prisoner schema, and the prisoner Schema always has these two blocks to generate. A block is the one that defines which client is going to be generated. The idea with Prisma is also for this to support working with multiple languages. And so right now we also have an experimental Go client, which can be used to generate a Golang client. But today we're focusing on JavaScript. And then second, we have this data source block in this data source block. What it allows us to do is it allows us to define the connection information to the database. And so I have here a locally running database Postgres database. And I haven't even created the schema in here. I've gone ahead and define these three models. And so it's worth pointing out that each one of these models maps to a database table, and then each row here. Hey, Dale, I'm very sorry interrupting you. Can you Zoom a little bit? Yes. Perfect. Take a look. I'll also make sure that my terminal is big. Great. I hope that's big enough in this model. Each of these fields actually maps to a database field. And so typically, when you start designing, you start building an application and you're doing it in a sort of domain driven design approach. It's usually good to sort of map out your problem domain and then start the actual data modeling before you start coding. And I think this is a really good exercise because it allows you to enforce you. It forces you to actually think of how you're going to represent the information in the database. And so where it gets really interesting is how we define these relations. And so here I've got a prisoner scheme that models a blog, and this blog has the concept of users. Each user can have multiple posts, and then each post can have multiple comments, and each one of those comments is obviously associated with the user who wrote that comment. What we have is these relation fields. These are actually virtual fields, because in this user table, and we'll look at that in a second, we don't actually have any foreign key that is pointing to posts or comments that actually happens on the other models. And so for example, the related field in the post is this author, which points to this user model. Another thing that's worth mentioning is that this is how you defined field attributes such as unique, so that will create a unique constraint, and this will set a default value, and the ID attribute will declare this field as the primary key. So let's go ahead, open up the terminal and then use Prisma migrate to actually create this database. So I have a Postgres database running here and I can actually enter it here Postgres. So that's the database. But we haven't created it yet. So I will go ahead and create it. And then the process of creating database schema, which Prisma migrate involves two steps saving the migration and then running the migration. When we save the migration, what will happen is it's loaded the prison schema and then it's saying that the database doesn't even exist. So we're going to say yes, we want to create it. And. We'Ll call this the first migration. And there we go. So the migration has been created. And now it's in this migrations folder in the Prisma. And that is essentially a snapshot of my database schema. At this point. Now I will run it using the Prisma migrate up command. And there we go. Now I'm going to connect to my database again with the CLI. And I should mention that PG is like the Postgres CLI, but it supports auto completion. So it's really nice if you work a lot with Postgres. And here I'm just going to connect to new JSD and I want to list the tables. And so here we have it the comment post user table. And let's have a more closer look at the post table. And so here we have the post table which has this ID. And it also has this author ID here, which is declared as a foreign key pointing to the user ID. And so this is really how the database schema maps to the Prisma schema. So that was the database migration with Prisma migrate. And now we will go ahead and actually write a script that makes use of Prisma. So here I've created this script file. I'll make this again bigger. I'll close the schema here. And before I do that, I actually need to run Prisma generates. So if you remember from the slides we said that we will create the database schema with Prisma migrant, then now we need to install and generate Prisma client. So if I open up the package JSON, you should see that I already have Prisma client here installed. And so the only thing that I need to do now is run NP Prisma generate. And what that will do is it'll. Look at my Prisma schema, the single source of truth, which was used for creating the database schema, and then generate a Prisma client that is tailored to our database schema. And so that is being generated. And now I can start making use of it. So just to make sure that it's running. And okay. So here we import Prisma client. We instantiate it. And here I've just done a couple of delete many because we might be running this multiple times, and we don't want to hit any any unique key constraints. And so our first to do here is to create the first user with two posts and include the relation in the response. And so to do that, I will go Prisma. And then you see, I already have access here to this user model and create. And I'm already getting this quite is quite rich auto completion. And so here I'm going to start passing the data. And if I'm not sure what am I allowed to create here, I just use control space. And the auto completion already tells me what am I allowed to put and in fact it also contains the type. And so the email is required. And so Simona at Prisma IO, Mona. And here I can also create these related fields. And so we want to create two posts. And so we're going to pass this post. And then for that we're going to create this object and we have two options. We can either connect an existing post that we have or we can create one on the fly, which is what we're going to do. And we're going to pass it an array and then open up the first object. You're not sure which fields we you can actually enter here again, control space and we have this is fully Typed based on our database scheme. And so let's give it the title. First post content a. Learn more about serverless with Asia and now I'm going to create another post here. Second post content. Learn more about testing serverless apps. Okay, I'm going to run. Oh. And assuming that I try to create here a field that doesn't exist, for example. Well, we have the published let's publish. Let's set the publish to true here for the first one. And assuming that I made a mistake here and I passed a string, this would be considered an error, and typically the error would be detected at runtime. But the beauty of this is that Prism is actually Prisma client is generated in TypeScript. Typescript has really nice integration with Vs code. So you can actually put this annotation here. This TS check annotation. What that will do is that will run the code through the TypeScript compiler in the back end and give us all sorts of errors. And again, this is purely JavaScript. I'm not introducing any type script here. And so here we get this squiggly line, and if we Hover over it, we say that type screen string is not assignable to type Bullier, and so we can do that. I'm also going to enable prettier, which I'm not sure why it's disabled. It looks like all my plans, and I will just run this. Okay, that's okay. This seems to be something there. Okay. So we've created this one user, and now let's try to run this. So I've got this NPM NPM run demo, which will run this script clean. And there we go. We get this first user. But what if we wanted to get also the relations that we created these two posts? Well, we have this ability to use include in order to fetch all of the relations. And so here if I do control space, the auto completion also helps me and I can set posts to true. And if I run this again, what we expect is to get the user and also the associated post with our Simona user. So that was all nice. Now what if we wanted to create a second user the same way that we created the first one but also attach a comment to one of the posts from Simona. Well, let's create a user for Thomas and then email will be Thomas Prisma. And then here we can go and create comments. And remember before we had connect and create. Well, this time we're going to use connect and then it's asking me for the ID. And this is the idea of the post that we want to associate it with. And so we can go to user one do posts and we want the first one. And here we have it. Now again, if I wanted to get the associated comments with this with this user, I could use this include and here I can pass comments. True. And so now if we run this, let's see what we get. Okay. Let's see. Alright. It couldn't be found. Let's see why that is. And so. Oh right. Yeah. So that was a mistake on my behalf. We actually want to create the comments and the comment will be. And then here the comment will be nice post and the post will be connected. And so here I pass the user one do posts, zero dot ID. Let's try to give this a go. Great. So we created the user and it attached a comment to this post. Now let's have a look at Prisma Studio, NP, Prisma Studio. And with Prisma Studio, as I mentioned before, we'll be able to have a look at some of the data in our database. And so I will open that up. And here I have it. And so we have these three models. And if I look at user, I have these two users and I can actually look at also the associated posts. Similarly, if I realize that I made a typo here, I can also use this interface to update that. And then I can also look at the comments and I can add records here. Nice post. And I can also link that to the second post. And let's try to I need to set the user who created that. And so that will be Thomas To and it's saving the changes. Yeah. It looks like that was created. Let's try to reload that. Yup. Okay. And now it was created. So let's go back to our slides. So what did we do? We created the database schema, we installed Prisma client, and then we looked at that script and saw how the auto completion and this fluent API work. We also created relations and then use Prisoner Studio to explore the data. But obviously we most of us aren't just writing scripts. We're actually building applications and API's like in the diagram before. And so I've created this slide to showcase how this would be used in the context of something like a rest API, which we have on the right hand side. And what we have here is we have this endpoint for a post and then that accepts the request body the payload of the request, and then it uses the press my client in order to create the post in the database, and then return that in the result. And then on the left hand side, we have a graph QL Resolver. So if you remember from Materials Talk, we have these resolvers when we build a graph QL API, and these resolvers are essentially the way that we resolve each one of the entities that are exposed by the or the models that are exposed by the graph QL API. And so this is obviously an example. And one of the nice things about Prism is that if you're using it in a graph QL API, it already solves the N plus one problem for you. And so if you're interested to look at more examples, I've put a short link. I will also share that in the chat where you can look at a lot more Prisma examples. And we have ready to run examples for various use cases both for JavaScript and TypeScript. Graph QL with the SDL first approach and the code first approach, we have some rest examples, a lot of different framework combinations like Apollo type, GraphQL Nexus, Schema, Express and XJS. And so before we wrap up, I'd like to say a couple of words about adopting Prisma gradually, because in this example that I've shown, we created the database from scratch. But the good news is that exist in projects that use a relational database like Postgres SQL or Mario D can adopt Prisma gradually using this feature called Introspection. And then the idea is again, we still have this single source of truth, which is the database, the Prisma schema. But in Introspection, what we do is we use this function called this method called Prisma introspect in the CLI. And then what it does is it actually introspect the database and generates the Prisma Schema for us. And so that way, if you have a running project that's using one of these databases, you can actually try it out. Today. You just set up the Prisma Schema, except that you only you don't need to define any of the models. You just define the data source and the generator, and then you run Introspect. Once you run Introspect, all of the models will be populated based on the database, and then you can generate and then you can generate the Prisma client and then have type safe access to your existing database. And so on that note, I'd like to thank you for the attention and just a quick point out that if you're using a relational database, Prisma makes you more productive and confident by reducing a lot of the manual work and also eliminating a lot of the type of errors so that you can really focus on the application by thinking in these JavaScript objects. So thank you. Hello. Hello. Thank you for your talk. Daniel, it was very, very nice. I really enjoyed the demo. It felt like magic. The developer experience looks very, very nice compliment. Thank you. We got a couple of questions in the chat. So the first one I want to ask you is so what is the difference between Prisma and a normal or? So there's a short answer in a long answer. The short answer we have that 15 minutes. Okay. Cool. So the short answer will start with that is that typically with or, what you do is you map model classes to tables in your database. And with Prisma, you don't do that. You don't need to define any model class in your application. And so that is sort of the core difference. The second thing is with Arms, there's generally two patterns. There's the data Mapper. I believe it's the data Mapper. And then there's the active record. These are two common Orm pattern. So arms aren't one thing. There's the two sort of flavors of it. And if we take, for example, active record on with active records in order to interact with the database, you typically instantiate one of these classes that you've defined that maps to your database table. And then you start modifying this object. And then you also have methods on these objects like Save or Persist, which will actually carry out the changes against the database. With Price, my client, you essentially import the client, and it's more like an advanced query builder. But I think because there's so many different flavors and variants of Orms, and because Arms existing in many different languages, it's not this one thing. And so in many cases, people misunderstand Prisma as being an Orm because they think that, oh, it makes it possible for me to fetch my data as objects or more. And then a health form. Exactly. Yeah. Okay. Makes sense. Thank you for the answer. And another question was what is the status with the MongoDB support? So it is on the road map. And I can also share a link to the roadmap. But it's something that a lot of developers have been asking us for a it's definitely there, but it's hard to say when that will be available. Okay. So another question I got for you. So I was wondering, how does Prisma work with Graph QL? Because you just showed us that I already solved for you the N plus one problem. And I was wondering how that works behind the scenes, if you can that. Yeah. Deep look into it. Yes. So under the hood, the way that Prisma works is there's this Prisma engine, which is actually a binary that is written in Rust and that is running. And that is handling a lot of the interaction with the database. And so the way it works is that there's this featuring type script called Venables. And the idea is that you could sort of have more control over the Promise API, how the actual Promise API works. And so the idea is typically, if I go back to the slide here. And so imagine that we had a user and we were fetching all of the posts for that user, which would be a typical case for the N plus one problem. Right. We're fetching one user and a nested post, which could have multiple. Oh, yeah. Could you share? Yeah. Exactly. Yeah. Sorry. So imagine that we have this user query. We're making a graph QL query. And then for that user, we're fetching all of its posts. And so in that case, that would be where the N plus one shows up. And for each time for each user that we're fetching, we have to make that extra query to resolve it. And so the way that that's solved is is that the queries don't actually execute immediately. Rather that once all of the queries have sort of been carried out, then they get batched to the database. And so that's handled for you automatically. And I'll also share a talk by one of the lead developers working on Prisma client on how Prisma actually solves the end plus one problem. And he sort of talks about this in more detail. Okay. Cool. Thank you. See a very last question. Unless there's some more questions from the audience. So you show up the the adoption of please, if you have an already working application by using what was the property name? Introspect introspection. Okay. Can you say a little bit more about it? Yes. Sure. So the idea again is that you have an existing database, right? Assuming that you have a Postgres database. So the only thing that you really need to do is you need to defining your Prisma schema the data source. And here, you know, that might be your existing database. And then what you do is you run. In fact, we could probably try this right now. Np Prisma, but we need to do that with the node JS one. And so again, the Christmas schema is now empty. And if I run Introspect, we see here introspected three models and wrote them to press a schema. And so suddenly we have these three models. And so now that we have these three models in our Prisma schema, we can go again, Prisma generate, and we get this client that we can import to our application and get type safe access. And so if you're using row SQL and you enjoy that, that's great. But I think that there's many situations in which you don't want to write raw SQL. Right. And so in those situations, it's probably you can do sort of a hybrid where you do some of the raw queries using SQL. And then for a lot of the very simple stuff you can use, press my client. But from my experience, once you start using Prisma client, it really covers so many of the cases of working with relational databases that there's not many situations in which I find the need in the applications that are built over the last year, I found the need to really write roles. Ql but you know, it's really a matter of preference then, but I hope that answered the question about Introspection, which was the big one. Yes, it did. You also actually just say something that I really enjoyed, which is basically one of the biggest problems of or, which makes like, 80% of the work super easy and 20% super hard close to impossible on what I just said is about the enhanced query builder is that you can use a Prisma. But if you really need to do SQL, you can just do it and it not be any problem. You don't need to have a fist fight with your exactly. And if you're comfortable with if you're comfortable with raw SQL, then you can do that. And you can also do that with Prism. And I've just shared the screen. Here how you would use Prism in order to do raw queries. Okay, it's very nice. Okay, well, thank you lot. Again, Daniel was very interesting and see you the next time. Thank you, Thomas. All the best. You too. Oh, my.