Server actions, RSC, Prisma, and TypeScript in NextJs 13

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we're going to talk about end-to-end type safety index.js 13 using react server components and Prisma we're going to also talk about data mutation using the new server actions all while creating this to-do app together where you can create new to-do's and or update these tasks as completed directly on the server without the need of having an API layer hey friends welcome back to the channel if you're new here my name is Hamed I'm a full stack web developer and here on this channel we talk about other web dev topics like react and xjs so let's get into this we're going to start a brand new Nexus application by running pnpx create next app with the latest flag I'm going to pass DOT as the current working directory as I've already created this next TS Prisma project it's going to ask us how we would like to set up our project I'm going to use typescript for this project eslintel and CSS no to Source directory and app router which is the recommended way to create next year's 13 apps with the Nexus 13.4 release which marks stability for the app router we're going to use import aliases and the add sign would be okay if you're going to wait for the dependencies to be installed and then we're going to jump into our code okay our dependencies are installed let's just start our development server by running mpmpm Dev or npm brand Dev if you're using npm package manager I'm just going to open the newly created project and let's just jump into our code over here let's start by deleting the boilerplate code over here so I'm just going to get rid of all this for now I'm just going to include a simple section with an H1 on it that says hello I'm going to get rid of this next image component in our Global CSS I'm going to get rid of everything over here I'm just going to leave the Tailwind CSS directives so we have a blank slate to start with I usually like to add prettier config to my project so bear with me to add this one for this I'm going to also use this Tailwind plug-in that allows us to sort our classes in a nice way so I'm going to go PM pm at Dash D I'm going to use prettier and this plugin okay let me clear this out and rerun the dev server let me close everything off over here and go to our home page so let's get started from our home page now if I save this I should see prettier kicking in now in our home page what we're trying to do if you remember from the application we just saw together we're going to try to fetch some to-do's from our database and show them on the home page and then we're going to have to have a form to enable the user to add new tasks and then each of these to do items would have a checkbox where you can just tick them off as a completed or done task so let's just set up our database we're going to use Prisma to connect to a mongodb database where I have already created some to-do's for us to work so the next step for us is to install Prisma so I'm going to stop the dev server run pmpm at Dash D and Prisma now that we're here I'm going to also add the Prisma client so we can use or generate a specific Prisma client based on our own schema once you've installed Prisma you can run pnpx Prisma in net this is going to initialize Prisma and create a directory inside of your application so you can Define your Prisma schema as you can see this was added here we can Define the database that we'd like to use in this project and any schemas for our data I'm going to use mongodb as part of this script this dot end file was also created for us where we can provide this database URL which is the connection string to our specific database in this case mongodb you can use any other database that you like now a side note here that dot end is not by default added to your git ignore so you can just come here and add this dot end here so that you're not committing this to your GitHub let me just close this off and here inside of our schema.prese mode let me just create this new schema or model that we want to use throughout this app and that's a to-do model which represents a to-do collection in mongodb and this is going to be ID by default it's going to be using the auto function and then we're going to map this to the field underscore ID from mongodb and this guy is going to be off type database object ID this is the first thing that we need here let me just close this off so I have more room the next thing we're going to add here is a title our title is also a string then we're going to have a is completed field which is a Boolean and this is going to default to false when we create a new task let's add a created at file which is of type date time and then we're going to default this to now and then we're going to have an updated ad field which is off top type date time and we're going to point this to updated ad so therefore anytime that our document is updated this field is going to be populated by Prisma automatically let me just paste in my connection string and we'll continue from there okay I've copied my connection string now to get started using prismo we have to create or generate a specific version of Prisma client that's tailored to work with our schema to do that we can run pnpx Prisma generate this is going to look at our prismada schema and create a specific version of Prisma client that we can use and share throughout our application to access our database as you can see here as part of the script it says you can now import Prisma client from the Prisma package and then we can create a new instance of this client to then connect to our database and perform our queries so let me just clear this out and run our development server the way that I usually do this or it's recommended by Prisma team is would create a lib folder inside of the slipboard folder I'm going to create a prisma.ts file now as you can see here if you're just creating a new instance of this Prisma client where we can share it throughout our application so any function that needs to access the database can use the same client as part of the process we're also providing this Global variable this prevents recreating this client if it already exists now going back to our lib folder another thing that I'd like to do is to create this to-do's folder where I can just put in all the functions that are going to work with my to-do collection now up top let's just import the Prisma client we just created and next we can create a function export async function we can call this get to Do's this is going to create this is going to return all the to-do's and we can provide a try catch statement over here and then we can say const to Do's this is going to use this Prisma client we just created and it's going to access the to-do model on it and it's going to find many and this is going to give us the to-do's and we're going to return our to-do's like so if we encounter any error we're going to return an object with this error property okay so let's just go back to our home page where we can just call this function to fetch or to Do's so I'm going to go back to the page component let me just close this Prisma schema for now as you already know any page or layout component inside of the app directory is a react server component so therefore you can turn it into an async function this allows you to fetch your data directly inside of your component so I'm going to say const to do's and I'm going to call evade calling the get to this function we just created this is going to import that function from our library or lib folder over here and let's just change this guys here let's just give this class a little padding of 20. let's also wrap everything here with a container so I'm going to put in a div with the class of container we're going to Define this in our Tailwind in a second I'm going to put an H1 that says to do is we're going to add some styling in a little while H2 would say maybe our previously added to those this is going to be previous to-do's and all we want to do down here is to render a UL inside of it I'm going to get our to-do's and I'm going to map over them and for each to do we're going to for now just render An Li where we would just show the to-do.title and we're going to pass in a key so to stop screaming at us and for the key I'm going to use the to do.id okay let me just save this out and if I refresh the page we should be able to see some to-do's over here okay as you can see we can see our list of to-do's over here now I'm going to add some styling to this let's start first by adding this container class to Tailwind you'd have to go to your tailwind.config.js I'm going to get rid of this extension that was added as part of the boilerplate I'm going to add this container it's just going to make sure that your content is centered and the width is going to change based on the device width now as far as the styling goes let me just replace this with the styled components that I have over here so you're not going to watch me type in telling CSS classes and I'm going to just pass in this H2 as well so I'm going to replace this over here it's just going to add some borders and some height and background to it and then let me just replace this unordered list with the styled version that I have over here as you can see we have this to-do item that we now have to create so let's create a components folder here I'm going to put it inside the app folder so therefore anything inside of it would be a react server component by default so I'm going to create a to do item over here TSX so all we are doing for now in this to the item is to re return that same Li that we were sending we were expecting to receive a to-do over here and then we're going to just then pass this for now the to do title over here let's just save this up let's just also import this over here as you can see this is going to our components folder and get the to do now one thing you can do to make your aliases even better instead of this dot not necessary what you can do in your TS config you can just add this Alias here because your components are now living inside of your app so you can create a new Alias that Maps everything inside of your components folder to this Alias so I'm going to go components and here this would allow me to just go back to my page and have an add sign here pointing to that same folder let me just close this up and go to the to-do item as you can see we have a typescript error here let me just get rid of this import react we can now create a type let's call this the to do item props and we're going to create a to-do this is going to be off type to do we can get this from our Prisma client and we can then say this is to do item props so we got rid of that error here now this is different with the way we used to do things in xjs 12 there first of all you were not able to fetch data directly in your components you would have had to use an extra specific apis like get server side props or get static props to fetch data on the server and then provide you to your page component which was running on the client and then if you wanted to type that data you needed to use a specific types from next.js to mark that data as something that needs needed to be serialized or converted to string and it was crossing from the server to the client whereas here with react server components up until this point that we haven't introduced any client-side component all the data fetching is staying on the server with all components rendered on the server nothing is crossing the boundary to the client it doesn't need to be converted into a string so the data doesn't need to be serializable and also if you're typing in the same way we're typing any response any fetch response or any data that's coming back from our database in this case because we are using Prisma it's already typed orm so we can get fully typed data back inside of our react component and then use them pass them to other props and we're going to shortly see that we're going to turn this to-do item into a client-side component and it just works the same way as when you are passing any other prop to your client components before continuing with the rest of the video I just want to take a moment to talk about my new Nexus 13 course that I've been working on recently where I would dive deeper into the new paradigm of building react applications using react server components the app router nested layout suspense we're going to be talking about routing we're going to talk about different types or strategies that you can use for rendering server side client-side Dynamic versus static we're going to talk about data fetching we're going to contrast this with the way we used to do them inside Nexus 12 and the Pages directory we're going to talk about route handlers or your API routes next year is a specific component authentication typescript and more also at the end as you can see here we have this e-commerce project that we're going to build together my plan for this course is to add a project per month to this so once you've done the main course every month we're going to add a new project that we're going to be using or building with Nexus 13 so that we're just going to learn more and more by building newly created projects so now the course is in video format that you can watch at your own convenience at your own pace anytime you want it is organized into different modules as you can see in different lessons if you're interested in learning lectures 13 with me by building a couple of projects there is a link in the description you can check it out and now back to the video now before diving deeper into this to do item component let's actually create a form that allows us to create new to-do's so for this I'm going to create a new component I'm going to call this new to do form and I'm going to create a react server component over here as well and all we want to do really here is to render a form and inside of it we want to have an H2 that says create and you to do and then we're going to have an input type text we're going to have a button of type submit that says add to do okay let me just get rid of this react import and bring this new to do form over here and then we're going to hook it up with a server action so we can actually create new tasks just close this off bear with me while I just add some classes to these components to make them look a bit nicer nothing fancy just Tailwind classes over here now we're going to put a name for this input because we need this when we're using server actions now with the introduction of server actions in extras 13.4 the way you would handle a form submission is different from the way we used to do it in Nexus 12 or Pages directory where you would create a handle submit prevent the default behavior of the browser and then submit the form data to an API layer where you would talk to your database and then return a response here we will just Define several actions which are just functions that run on the server and can be called from the client so let me create an action over here I'm going to call this also an action so I'm going to then defining action this up top over here I'm going to say this is an async function called action this is going to receive some data and this data is going to be of type form data and we're going to get the title which is this input over here by accessing data.get and we're going to pass the title we're going to say if there is no title or the type of title is not a string we want to return from this function and otherwise we want to just call a server action to create a to do and then after that maybe we want to reset the form so therefore we've bypassed the text inside of our input so let's start by calling a server action that creates a to do the convention for creating a server action is to create a file called underscore actions inside of your app folder here we're going to pass in a use server directive up top this indicates to the xjs that the contents of this file the functions and modules are only ever supposed to be run on the server we're going to then export an async function over here we're going to call this create to do I'm going to just say creative action so if we don't confuse this with other creatives that we're going to have to create to to talk to our database so it takes in a string and all it does is is going to call a database function that created to do now for this I'm I'd have to go back to this to-do's and actually create a function here so I'm going to say export async function I'm going to call this create to do this is going to get a title of type string and all this is going to do is it's going to use our Prisma client and is going to access the to-do model and it's going to create a new document with this title once we have this we're going to return this to do and if we don't have it let me just close this off we're going to return an object that has this error property so what we're doing so we created a new database access object or function that takes in the title and calls the to-do create on Prisma client passes this title as the data to create and you record a new document inside of our collection okay we're going to have to call this create to do from inside of our server actions so we created this create to do action which is a function that runs on the server can access the database with the function that we just created these server actions and what's special about them is that you can call them from client-side and then they're going to then perform this mutation or update or data creation here on your server now all there is left for us to do here is to import this create to do function we just created and pass in the title to it now I could have read the logic of accessing our Prisma client right inside of my action here because this is also running on the server I just like to keep them separate so inside of my to-do's file I have all my database access objects or functions and inside of this actions file I have all of my actions so they're nice and separate so from anywhere inside of your applications from inside of any function if you want to create it to do this function is exported from here you can just call this create to do and pass in the title and that will create a new to-do for you okay now that we have created this server action let's just go back to our new to do form and actually use this server action first thing that I'm going to do is to turn this into a client component by using this use client interactive up top so therefore I can capture the form data that was submitted by the user from the client side and call the server action that can create the to-do task on the server side so now before we can use server actions though we need to pass in the experimental flag to our next config.js because server actions are still in Alpha version so all you need to do is to pass in the experimental flag and then Mark server actions to true this is going to restart it's going to detect the changes in your next config but I like to just stop and re-run the dev server manually over here to pick up on the new change once we have that we can go back to our form and then all we need to do is to just import this create to do action we just created this is our server action and I'm going to pass the title to it with that if I just go back to our form and create a sixth to do task over here and hit submit this is going to submit our form but there's a couple of problems here first of all this needs to be wiped out this is what we're going to do by resetting the form but more importantly we don't see this newly created task if it was created at the bottom of our list now just to confirm that this task was actually created if I open up my mongodb and refresh now as you can see this sixth task was created actually inside of our mongodb it's just that it's not shown on our application now what we need to do when we are performing a server action that does a mutation on the server we need to actually revalidate this path that we are on so that we are refetching the data from the server to show the newly created task so what we need to do is to just call revalidate Path this is a new function from next cache and we're going to just pass in a string pointing to the path in this case the home page that we want to be revalidated once I save that you can see this uh last task popped in over here and if I now ask add another task over here seventh to do task and hit submit you can see this new task is now buy this revalidate path is refetched from the back end from the database and then re-rendered over here now to wipe this content of this path out for that I can use a ref Hook from react we are inside of a client component so we can use react hooks so I'm going to call this a form ref I'm going to use the use ref hook I'm going to initialize this to null I'm going to also give this a type of HTML form element and then down here I'm going to pass this ref to my form pointing to form ref and then from inside of our action once this mutation was done on the server I'm going to access this form ref the current property on it and then I'm going to call reset to just reset this form now this obviously may be null so I have to check to see if I have a reference to the form before I'm calling the reset function on it now with that out of the way if I create the eight eight to do task and then hit enter not only I'm seeing that new task at the bottom of my list but also this form was wiped out with this reset okay with this out of the way let's just go back to our page and work on these to do items where we can add in this check boxes here so we can Mark these tasks as completed so let's dive right into the to-do item let me just copy paste some Styles over here so you don't watch me typing this so all I'm doing here is I'm rendering this input type checkbox I have a default checked value set for this to-do is completed so when the first time we are rendering this to do tasks if it is completed it's going to be marked as completed we have a label for it and then we have this span which shows the updated ad property of our to do and then turns them into this UTC strings now I've used some telling classes over here you can look through to just strike through this task once they're completed but right now if I select these they're going to be marked as completed just off from The Styling perspective but if I refresh the page this one is not actually communicating to our backend it's not actually triggering any server action or mutation inside of our server side and that's what we're going to hook up to this to-do item to be able to do now another way we can call server actions or perform mutation in Nexus 13 is with the use of the use transition hook so if you go to the next JS documentation and go to the docs here if you search for Server actions you can see down here that there are different ways that we can call these server actions as you can see the invocation section talks about the action property this is how we did it inside of our form so we had this action property set to an action function that calls a server action now the other way is to use the form action attribute inside of a button or input Type image now let me make this a bit bigger as you can see down here there is another way to invoke your server actions from client components using the sort transition function that's returned from use transition hook so as you can see here we have this use transition Hook from react when you call this inside your components this is going to give you this start transition function which you can just bind to your on click or even handlers inside of your buttons or in our case the input type checkbox so that we are starting this transition and then calling our server mutation from inside of this start transition callback okay let's actually use this inside of our to-do item over here so all we need to do is to use the use transition hook this is going to give us the ease pending and the start transition function so we need to get this from use transition the import from react is happening there and what we need to do over here is to just come in down here and say on change of this checkbox get the event and inside of the event we're going to call this a start transition start transition is going to take in a callback from inside this callback we're going to have to call a server action so let's name This Server action update to do action and this is a function that we want to call we want to pass in maybe to do ID to it and then we want to also pass in the E which is the event that happened Target and the checked property so we're going to access a specific to do based on this ID that we are passing by this is passed to this to do component so if you're accessing this is specific to do and then the e.target.check is just going to read to see if this checkbox is now checked or not if it is checked it's going to be completed if it isn't it's going to be marked as not done or not completed so let's create this update to do action which is a server action together going back to our action over here so we're going to export an async function called update to do action from here that takes in an ID which is a string and is completed which is a Boolean and from here we're going to perform the update using the Prisma client and once this is done we're going to revalidate this path so for this let's actually go to our database access functions and create a new function we can call this update to do this is going to take in a string as the ID and the same properties that we just had there this one has a Boolean if I can type and then all this is going to do is to actually access our Prisma client so we're going to call Prisma going to access the to do we're going to then update we're going to say where ID is this ID and then we're going to pass in the data and then is completed as our data once we have this to do we're going to return this newly updated to do and if we encounter any error we're going to have to return an object with this error property like so so all we're doing inside of this database access function is we're getting an ID and is completed property we're going to call Prisma and access our to-do collection performance performance update query on our to-do collection find a document where ID is this idv passed it and then pass in this data this is the actual update we wanted to perform we're going to pass in whether or not this specific to do task is completed or not so now that we have this function let's just go back inside of our action and we're going to call that update to do we create it we're going to pass the ID as and is completed that's coming from our client side to This Server action and then once we do this we can just call in revalidate path and revalidate that same path because if you are on our home page so let's go back to this to do item now I can just import this to do action we just created inside of our client component as you can see I haven't turned this into a client component and this is giving me an error saying that I'm using use transition inside of a server component so it's just go up top and add this use client directive with this we are able to not only use this use transition hook but also use this update to do function let me just try to import this one more time there you go now with this out of the way if I just go ahead and Mark this third to do task as completed it is shown inside of our front-end or client side but if I do refresh the page this is actually showing that this task is now marked completed inside of our database and I can confirm this by opening up my mongodb and actually refresh this going to the third task that we have and this third to do you see this is completed that is now marked true now one thing I want to mention when using start transition or the use transition hook as a way to call your server action is if you go back to the documentation you can see here using this dot transition is going to disable the out of the box Progressive enhancement that comes with several actions now what this means is the server actions actually work without any JavaScript so the way it works is that the initial HTML that goes to the client is going to work if you're using the action attribute on a form it's going to work even if there is no JavaScript there so it's going to submit the form data to that same server action that you've defined and react is going to progressively enhance that form with JavaScript when it is available but it works out of the box without JavaScript but when your mutation is depending on start transition or this use transition hook there is no other way for it to work so you have to wait for react to hydrate this component before you can use this actual form or button or however you're hooking this up to perform that or to execute that server action and perform the data mutation that's a wrap for this video folks we created a to-do app using the server actions that allow us to send data directly from the client to the server without needing to create an API layer to handle form submissions more importantly we use Prisma which is a typed client for a database in this case mongodb to have end-to-end type safety as you saw we were able to fetch our data right inside of our react server components thanks to Prisma this data is coming back already typed for us we then pass this to-do's down into our client-side component which is this to-do item that adds some client-side interactivity they were able to update this specific tasks that's completed or not we also looked at this new to do form that again uses some server-side actions to perform data mutation directly on the server if you have any questions hit me up in the comments if you're interested in learning Nexus 13 I do have a course where I dive deeper into these concepts by building a couple of projects the link is in the description and I will see in the next one bye
Info
Channel: Hamed Bahram
Views: 11,141
Rating: undefined out of 5
Keywords:
Id: 8e35eo447Zw
Channel Id: undefined
Length: 34min 27sec (2067 seconds)
Published: Sun May 28 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.