Uploading files in NextJs 13 using Server Actions

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we're going to look at uploading files in Nexus 13 using several actions 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 in this channel we talk about modern web topics like react and xjs so let's get into this we're going to build on top of a project we created together on the channel where we used react Drop Zone to create this drag and drop area so users can drag and drop files over here and once they do we're going to show them a preview if the file is accepted in terms of the file size or type we're going to quickly look at this settings and we're going to reject them if it is for example if I drag and drop a big file over here it is rejecting that file that I just dropped with an error that says it's actually bigger or larger than one megabyte now for that package or project we use react drop zone that project was using Nexus 12 Pages directory but the setup for the react drop zone and the apis and the hooks are identical to this project I will review what we're doing from a high level but if you want to dive deeper into how to set this up you can actually watch that video I'm going to include a link in the card somewhere in this video we're going to build on top of it and it actually comes where we want to upload to Cloud energy now cloudinery is a cloud storage service for your images and we're going to use a signed or authorized URL or upload to Cloud internet and we're going to perform that using server actions from our backend so I'm going to explain how we're going to do that but if you wanted to start from before we get to this point you can watch that video Let's just jump into our project over here so I created a next JS project by running pnpx create next app with the latest flag this is not using typescript it is using JavaScript and it's set up already to work with telman CSS I'm going to include a link to the source code so you can follow along while we're building this together now I've already created this project so I'm going to walk over the code and actually what I have done instead of writing the code from scratch now inside the app folder we have the layout this is the outermost layout or the root layout of our application I haven't done anything to this this is what comes out of the box when you run create next app now let's jump into the page this is a react server component that represents our home page or our index page let me just close this off so all we're doing over here is rendering this H1 that says upload files and then this a custom Drop Zone component that I have created so let's just dive into this Drop Zone component and see what we're doing over here so from a high level what this is doing let me just close this off so if you're not distracted this functions I'm going to explain what these do but from a high level what this is doing is this is rendering a form and this form is rendering this input and this uh div area which allows a user to drag and drop files this is coming from the react Drop Zone package and then down here we have a preview section where we have some accepted files and then some rejected files down there based on the criteria that we would Define together now the way react Drop Zone works is that it will give you this used drops on hook that you will call inside of your component and to be able to use this type of hooks we have to run them inside a client component therefore we have that use client directive up top to instruct nextures that this is a client component because by default every component page or layout inside your app router is a react server component now this is going to give you some helper functions that you can just call and spread over the div that you want it to be the drag area and also over an input where you want to have an input type file now you can pass some settings to this use Drop Zone hook for example I have said that I only want to accept images but any type of images I have defined a maximum size of one megabyte and a maximum file of one so I'm not allowing multiple files but only one file it will be the same way if you're also working with multiple files but because you have to Loop through the files and then upload them one by one to cloudinary I'm just working with one file but you can just go ahead and have as many files as you want the logic will be the same now for this on drop this is uh what happens when we actually or the user actually drops some files so if you have created this function using the use callback Hook from react so we are not recreating this function we get the accepted files and rejected files based on our criteria and what I'm doing here is that I'm holding these files and rejected files in local state react State now what I'm doing here is that I'm getting the accepted files I am mapping over them and for each one I'm creating a preview URL so that it allows me to actually preview them in inside these accepted files so at this point when the user drops a file and we show them a preview we haven't uploaded anything to cloudinary or to our backend it's just using the browser API to show a preview so that's what we're doing inside here and this line over here if you're allowing multiple files you don't want to override the previous file so you can just extend the previous files but because I'm using only one file so I have commented that out that you can bring it back on if you wanted to have more than one file and I'm doing uh something similar with the rejected files we're not going to have any preview for the rejected files I'm just holding them in state so I can have them down there and show the error and allow the user to actually remove those files so that's the on drop function the rest of this actually is helper functions that we can use to remove a single file all the files or the rejected files now what's interesting here or is US building on top of the project we previously had is what's happening when this form is submitted now in that previous project we had an on submit Handler attached to this form where we would prevent the default behavior of the browser read the file that was selected and then use cloudinary preset uploads to upload that file to cloudinery and get a URL back now in that video I used an unsigned or an unauthorized way of uploading images to cloudinery which is done through presets so look at that video if you're interested in that but now in this video we're going to make an authenticated or signed request to cloudinery so it's more secure plus that we're using actions instead of submit handlers inside react so let's dive into this action function that I've passed into the action prop of this for if you're not familiar with server actions and how they work I do have a video on the channel where I dive deeper into server actions and data mutation in next year is 13. I'm going to link it somewhere in the card so you can watch that and then come back in here but but basically we would Define functions that we pass into the action prop of a form these functions are going to perform mutation or they're going to perform an action on the server or on the client using server actions so let me explain from a high level what we're trying to do here and then we'll look at the implementation now when we are trying to make a signed or authenticated request to cloudinery the first step is to create a signature now a signature needs to be created on your server side because you need to have access to your secret key now with Cloud energy you have a cloud name you have an API key and you do have an API secret the cloud name and API key can be exposed to your front end as we're going to see in a second but your secret key needs to stay on the server so the first step is to create a signature so once we have that signature we're going to make a post request to Cloud ordinary from the client side using that signature to actually upload our file and once that's done we get the public ID back we're going to verify that public ID with our signature again inside of our backend before saving that URL or public ID to our database okay let's now actually dive deeper into these functions and see what we're doing over here first thing we're getting the first file inside of our files estate this is the accepted files that came back from react drop zone so I'm getting the first one out if there's no fault I'm returning obviously we don't want to upload if there is no file and if there is a file the first thing that we need to do as I explained is to get a signature from cloudinery for that I have defined a server action called get signature that I'm calling to give me a signature that I can then use to upload let's actually look at this function so the convention for Server actions is uh you would create and underscore actions JS inside of your app folder and underscore makes this file private so it takes it out of your routes and segments a convention to use to organize your actions you could use this use server directive up top to instruct next.js that these functions are only meant to run on the server and then you can include all of your server actions like this get signature or this save to database in one file so let's look at this get signature and then we're going to look at that one later so for this we're going to import cloudinary so you have to install the cloud energy node.js SDK this is going to allow you to actually create a signature so if you're going to set the config for our cloudinary with our Cloud name API key API secret and secure which is only making this cloud ready to work over https so I've included a DOT end dot example and these are the environment variables you need to set this is the upload URL you're going to use this from the client side when you wanted to actually upload you're going to bring in your Cloud name API key and API secret now the cloud name and API key are allowed to be exposed to the front end or client side of your application that's why I have prefixed them with next public but your API secret needs to stay in your backend that's why I've mentioned secret here and public there and you can get them from your cloudinary dashboard again if you are not fully familiar with cloudinary watch that video that I talk about react Drop Zone in there I show quaternary dashboard and how you can get these values bring them here into your local environment so you can set the config over here once you get this cloudinary SDK so this is now configured to work with your specific cloud now inside this get signature server action we're creating a timestamp which is now and we're going to call this API sign request and we're going to pass in our timestamp I have also included a folder because I want to save these images that users upload to a specific folder called Next inside my cloudinary account and then as the next or second argument I'm going to pass in my API secret this is going to give me a Signature Pack I'm going to also include this timestamp I'm going to return this back to my front end here I'm getting this timestamp and this signature out of this get signature function and what I'm doing is that I'm creating a new form data I'm appending this specific file that I had now the other stuff that you need to append to this form data is your API key again I'm using next public cloudery API key this is why we prefixed them with next public because we need to use them inside of our client side remember this is a client-side component of your API key again and cloudname are safe to be on the client side but never expose your API secretly now we're going to also append this signature that we got back from our backend we're going to also include the timestamp and also this folder that I want this specific files or uploads to be saved inside my cloudinary account now again we had this upload URL which is also coming from r dot and this is going to be the API endpoint for cloudinery you'd have to paste in your Cloud name over here this is going to allow you to upload only images to your account so I'm going to make a post request to that endpoint and the body would be this form data we created over here and once this is done and the image is uploaded to Cloud energy this is going to give me back some data including a signature a version and a public ID a secure URL to actually access that specific image now the last step before we actually save this data to our database is to verify this information coming from the client side so I have this save to database server action I'm going to pass in the version signature and the public ID the public ID is the public ID of this specific image you can access it through a secure URL but before I save this to my database I want to confirm or verify that this is actually coming from cloudinry so inside this save to database as you can see over here what I'm doing is that I am getting this public ID that the client-side application or user is sending me and the version and I'm going to sign this again with my API Secret and then I'm going to compare this expected signature that I just got out of signing this these two values with my API secret with the signature that the client side or the user has sent me in this actually function call so they're going to send the signature the version and public ID I'm going to sign the version and public ID with my secret that the user doesn't have access to and then I'm going to compare whatever I get that signature to this signature they're also sending so that I can verify that this is actually coming from our cloudinary account once this expected signature is equal to the signature that the user is claiming to be the signature I can go ahead and safely write this to the database now to test this out if I now go ahead and refresh this I'm going to go ahead and actually drop this image again we can see this image down there and if I hit the upload Cloud energy button and if I open the console over here you can see in the back end I'm logging this public ID this is just logging it down there and if I go to my cloudinary dashboard and refresh we should be able to see this image now uploaded to the next folder inside of our cloudinary account now the benefit of using a signed or authenticated request to cloudinary is that users won't be able to upload images to your account without a signature that's actually created inside your backend so they don't have access to this functionality on the client side and also inside this get signature you can Implement any other check that you want you can check the session inside this get signature and only create this signature if the user is logged in now let's quickly contrast this with how we would do it in Nexus 12 or Pages directory or without server actions so up here when we're calling this get signature this is actually replacing an API call so if this server action didn't exist we would have to create an API endpoint for generating these signatures from here we should have just caught that endpoint get that signature make this upload using that signature and then down here for saving this actually to the database we would have created another endpoint to actually verify and save the data to our database so we have eliminated the need to create two different API endpoints to get a signature or to save the image actually to our database and replace them with these two server actions which are just two functions that run on the server and you can call them from your client-side that's a wrap for this video folks we looked at uploading files index.s 13 using server actions cloudinary signed and authenticated requests and react Drop Zone if you have any questions hit me up in the comments if you're interested in learning nextges I do have a course the link is in the description let me know if you have any questions and I'll see you in the next one bye
Info
Channel: Hamed Bahram
Views: 20,193
Rating: undefined out of 5
Keywords:
Id: gW7DSJ2a6pg
Channel Id: undefined
Length: 17min 32sec (1052 seconds)
Published: Sun Jun 04 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.