Next-Level S3 File Management: The Ultimate Guide to Handling Files in Next.js 14

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
this is a basic social media app that I set up with next where a user can post an image or maybe even a video and send that in a post to the server and it's going to upload this video file or image file directly to an S3 bucket while storing the rest of the information in a database and then it can redirect me to the homepage feed where I can scroll through my post I can view my post and I can even delete my post which will delete it from the database and the S3 bucket so I'm going to show you how to set this up where you can store your files in an S3 Bucket from a next application so this is the finished version of the application and here's some of the code that actually handles the file upload but I'm going to go and check out the start version of this and run that and we can see that there's uh still a lot of code in here much less code CU it's basically this app but it doesn't do anything if I write anything in here it doesn't actually post to a server if I select an image nothing really happens here so we're going to focus on building out the parts of this app that actually upload files to S3 and then interact with S3 in the database if we look at the form we can find the input where the user actually selects the file and we need to keep track of this in state which is slightly different than the way we manage State for the text input so the first thing I need to do is still create a state variable so file actually want null to be undefined and then when the user actually selects an image the onchange will get called and I'm going to make a different function for this so handle change uh put this up here and actually I'm just going to set this without an if statement so whatever the file is uh the user selects maybe they select nothing maybe they select a file we're going to set that to be this actual file object up here uh and then when I go to submit this the handle submit um I should have some content that was created in this text input here and my file so I'm just going to console log both of those out right now uh content file so this isn't doing anything I just want to make sure in my console that I've actually got those bits of information so I just have whatever I type in there and I should just see some sort of file object dumped out into the console so let's see so I can see that I have a reference to the file and the text there and the next thing I do before I send this anywhere I actually want to be able to see the file here I think that's just a nice feature to have uh it's definitely optional but I still think it's nice so I'm going to create another state variable here and when the user selects a file there is a URL that we can associate with it locally just in the browser uh and actually I'm going to bring this out into its own object here so const file equals so we'll set the file to be the file object uh if there's a file we can generate a URL just a local URL and then use that URL to present it in a image tag or a video tag depending on what type of file it is uh so if there's a file that else um oh wait need to set the file URL uh if there's no file then we'll set it to be undefined and if there was a file URL already created yeah we should revoke the object so a little bit of code but that will essentially give us a local URL to the file so we can present it in the browser and then here I have a comment where I actually want to present this so uh if there's a file URL and we actually have a reference to the file then let's see what it does for us here that might be too much am I change Chang this to an image tag but let's see what that does without making any changes so if I put an image in there copilot even put a remove button in there so uh how does that even work where is the remove button okay uh onclick set for oh that's kind of cool all right so it's actually given us that's nice okay uh and if I put a video in there that should also work oh and okay The Styling is not great but that was that's pretty good for co-pilot um okay brilliant so now I can select a file and I can add a little caption here and now I need to figure out how to send this file off to an S3 bucket but before I can do that I actually need to go into AWS and create that S3 bucket so if you already have an AWS account you can log right in and do this if not just go create an account and then log in and follow along uh but essentially I'm going to navigate to the S3 console and create a brand new bucket for this application so I'll create a new bucket I'll call this threads clone local Sam it needs to be a unique name a globally unique name I hope that's globally unique and then select a region probably the one closest to you and your users I'm going to go with Canada cuz I'm in Canada and actually immediately I'm just going to throw this into my environment variables cuz we're going to need it later anyway uh so I'll just call this AWS bucket name there we go uh and and then let's scroll down we're going to leave these default settings uh we want to uncheck block all public access and we want to check that I acknowledge that the current settings might result in the bucket and the objects within becoming public because the way we're going to set this up from the next app is that the client is going to be able to upload directly to the S3 bucket because we don't want large files like a 10 megabyte video file going through our nextjs serverless functions or our Edge functions so it's going to go straight to the bucket and then when we get these images or videos or whatever file you're uploading uh you want to be able to access them straight from the bucket itself so this is going to be public and we need to acknowledge that here so we don't care about versioning or Tags by default everything's encrypted on the S3 bucket which is kind of cool and then we can just create the bucket and then down at the bottom here I can see my bucket so I'm just going to click on that and I can see that this bucket's empty and before we can start using this Bucket from our next app we need to modify some of the permissions so we go to the permissions Tab and go to the bucket policy going to edit this add a new statement for S3 and this is just going to be for getting objects we need this policy get object because we need to allow pretty much anyone or anything to be able to get an image or a video or whatever file from the S3 bucket um so I'm going to manually type this out star right here means anyone or anything uh can get an object and the resource is just the bucket we have so let's see add a resource uh I'm going to say the bucket name is the thing I just copied and object name is anything so any in the bucket will be able to be accessed as long as someone has a link to that object so save these changes and I'll add the code like this to the video description so you can just copy and paste it straight in then if we scroll down the other thing we need to modify are the core settings since this is going to be accessed from the front end of our application we just need to enable that so I'm going to edit this and for this one I'm just going to paste it in so we have put and get as the methods put is for uploading a file get is for getting the file uh the origin I'm going to change this to be uh Local Host 3000 and basically this applies to this individual bucket and I've called this bucket local because it is the bucket I'm going to use when I'm testing on my local local machine this means that you'll have a separate bucket for your production version of your app and you'll have to put your production URL into the settings of that separate bucket uh but I'm going to save these changes this is good for my local instance and now the bucket is created we don't need to do anything else with it uh just make sure you have access to the name and its location that's actually another thing I'm going to write down immediately AWS bucket region I put mine Inca Central one and the next thing we need to do is create credentials to give our application permission to actually access this bucket so I am going to open up the IM am service here I'm going to open that up in a new tab and we need to create a new user and users in I am can be real life users with a username and password or they can be applications that have an access key and a secret key kind of like dealing with any other API that has keys again this is my local application so I'm going to call this uh threads local user maybe and then I'm going to click next and here I want to attach policies directly and you will hear a lot of people tell you to just use the Amazon S3 full access policy but that would give your application complete access to create and delete entire buckets or objects within buckets and you don't really want that you only want your application to really be able to add things into this specific bucket and then maybe also delete things from that bucket so we're going to create a custom policy over here we're going to click that button and we're going to specify exactly which permissions this user should have so I'm going to select S3 from the list and the actions let's see if I can filter these correctly so we want to put an object that's creating a new file in the S3 bucket and then deleting an object seems good too and just those two actions we want to allow so then I'm going to click uh let's see add Arn and we're going to specify the bucket name uh but we're going to allow this to be on anything within that bucket so any file in the bucket buet can be created or deleted by our application uh and that looks good I'm just going to check the Json here that looks good to me so then I'm going to click next uh policy name let's call this threads local bucket policy and create that policy and now I can close this tab go back to the other Tab and if I refresh these I should be able to see my new policy yep there it is threads local bucket policy so I'm going to select that hit next and create the user so I now have this user threads local user and I just need to generate the access keys for this so I am going to go to let's see security credentials and create access key and then I'm going to select application running outside AWS CU that is our nextjs application and we'll get this little warning here alternative recommended use IM rolls anywhere instead and this is is telling us that there is a better way of creating credentials to access our bucket uh but it would take about another 20 minutes to get this set up and I'm pretty sure it costs about $400 to have Amazon manage our private certificates through this workflow so it might be worth something looking into at some point but right now this is more than sufficient so I'm going to treat this more as a warning and click next because that warning is telling us that we should be careful with our keys so when I go to create these access Keys here these should always be kept secret you shouldn't share them with anyone you shouldn't commit them into Git You should probably figure out how to rotate them every once in a while they shouldn't really be stored in environment variables but since this is an next app that will probably be deployed to ver cell we're going to just copy these straight into the environment variable file just make sure you don't share them and remember that you'll need to go through this process again to create different credentials for your production bucket when it actually comes time to deploying this app so I'm going to put this in as AWS access key and then we have the secret key which I'm going to copy and you're going to see but I'm just going to delete this after anyway so it's kind of okay access key these are the four environment variables that you need the name the region the access key and the secret access key and now I'm done with this I think I'm good I don't need Am anymore but I am going to keep this bucket window open so we can monitor as files are actually being uploaded now we want to have the form send this file straight to S3 when we submit it but we can't just give the client the browser complete access to S3 or users would be able to upload whatever they want or delete whatever they want so instead we need to have this communicate with our own server our server can generate a secure URL that just allows uploading of this individual file and then this component right here in the handle submit can send that file directly to S3 but first we need to communicate with our own server so what I'm going to do here is I'm going to create a new actions. TS file and we're going to make a server action called uh get signed URL and this is going to return a URL that the client can use to send the file to S3 so right now I'm just going to return actually I'm going to return a success object that has a URL string that's just going to be an empty string for now this is the structure that I want and because this is a server action I'm going to put use server up here and I already have orth implemented in this app using next or so I'm just going to quickly drop that in to let's see from at or so essentially what I'm doing right now is making sure that the user is logged in before I even allow them to request a URL so that they can upload uh a file uh and if there isn't I'm going to send back a face faure um not authenticated sure but since I'm logged in I should get back this success object so now in my client I should be able to import that server action from actions and then right here I'm going to try calling that uh so let's say signed URL result equals await get signed URL and let's just log this out and see what kind of response we get back from the server so I'll just yeah submit this again uh we got content and we got success and there's just an empty URL so the server action is working and I would just want to be able to grab that URL so URL equals signed result. success. URL and I'm going to do a quick check here if signed Ur result. failure there we go something went wrong maybe it wasn't authorized maybe something else happened uh I am just going to set my status message to fail uh set loading false and let's console [Music] error error there we go that's good uh actually I need to make sure that this is not undefined there we go all right so now this works so if there's an error handle the error otherwise we have this URL and then we can use that to put the file to S3 but first we need to make sure this is an actual signed URL so this is where we need to start working with the AWS libraries and the first thing we need to do is install at AWS SDK client S3 and then we also need to install S3 request preer I think that's what it is yeah it looks good then back in the server action I need to import S3 client from there we go and then I need to set this up which is almost correct I think so these need to be the environment variables we have I named them slightly differently so put those in and for the region I'm going to use an environment variable too process yeah there we go uh I think I called it bucket region though make sure that you spell the correctly cuz that can be annoying so we can use this object anytime we need to do something with the S3 bucket I also need to bring in the put object command so we're going to put a new file into the S3 bucket then down here once I've done my checks I'm going to create a new put object command and this needs a couple of different parameters first is the bucket name and the second is the the key so that's basically the file name as it appears in S3 so I'm just going to call this test file for now and those are the only two we need we can add more things later and we will but that's all we need to get set up for now and then I'm going to bring in the other Library so import get signed URL and then we're going to create a new signed URL from those objects and I'm actually going to expire this in 60 seconds so that means that we're going to create this signed URL and actually let's just take a look at what this URL looks like so we're going to bring this in I'm going to hit post and I think I'm not logging this out anymore console log URL so let's go back do that again and here's the URL so this is the signed URL all of this stuff let's actually just grab this and and see what it looks like so you can see let's word wrap this uh this is a really long signed URL it has the information about the bucket it has the file name of the file I'm trying to upload and a whole bunch of other stuff that actually gives permission to my app to upload this file and when I specify that this URL expires in 60 seconds that means that my client application has 60 seconds to actually upload the file and that can avoid some malicious uses of this URL so that all seems to be working so the only thing left to do is actually use the URL uh in a fetch request so let's see we're going to use that method's going to be put the body is going to be the file and we're going to specify the content type and I don't think we need this although I am realizing that file might not even be a thing here so we should probably do another check I guess right around here um make sure that I do actually have a file selected before I go and generate the signed request and do this put request and I'm going to update my status message that the user sees uh to be uploading file so let's see if this works this part of the code that uses that URL that signed URL that we generated should upload that file so this image should get uploaded to the S3 bucket uh so I'm going to check this three bucket it is empty there are no objects in it if I hit post here it should now send it off to S3 um but it seems like there was a cause issue so I may have messed up the permissions let's see um yeah I'm pretty sure I messed this up this supposed to be HTTP Local Host 3000 let's save those changes and try again I'm going to have to refresh this page cuz I don't have proper error handling that is something I should probably add in right now let's post this seems to be uploading the file I'm going to check the network tab uh okay it's doing that oh yeah okay so it said created that's looking good yep the put request 200 okay and if we go back to the object let refresh and there is my test file and actually if we select this file in S3 we should see the object URL right here if I open this I'll be able to see that I can access the image from the bucket so that's pretty good this is actually a public URL that I could share with anyone so anyone can see this file uh so that's great I now have the image uploaded to S3 and I'm able to access the image from S3 too um while I'm thinking about it though I am going to implement a small amount of error handling here I think I'm just going to wrap this whole thing in a tri catch um I'm also going to throw here if there's an error I can remove this loading state and yeah I'm going to handle all errors in one go here which I don't think is a good method but it should do the job and I'm going to take the loading State put it in a finally block don't know why it's returning here okay um let's also yeah there we go cool all right um that at least help me handle errors so uh but this seems to be working that's great but we're not doing anything to sure the user doesn't upload something bad and what I mean by that is really they can upload whatever file they want right now they can upload any size of file and we really want to restrict that to only be images and videos that meet our requirements so we're going to add a little bit extra logic here and in the server action we're going to do a couple of checks and the first check is actually going to be for the file type so if we scroll down to the file input I can see the different different files that I'm accepting client side just in the HTML uh I'm going to make sure that the file is definitely just one of these things before even generating the URL so I'm going to say um accepted types uh I'll turn this into an array uh let's just separate on the commas then we're going to require the client to send in which type it is so we'll say the type is a string and then we'll do a quick check if uh accepted type includes if accepted type does not include the type of the file they're trying to upload it's an invalid type uh we can also check for the size so let's say we have a Max file size um of 10 megabytes is that actually 10 megabytes I think yeah okay then we can also have the client pass in the size yeah going to make that a number and we can double check that and then I'm going to bring this down obviously uh so that only gets signed if those requirements are met so this by itself is just the client telling the server what the type is and what the size is and we only generate a URL if those things match what we have but the client can still lie about those things so what we're also going to do I need to bring this part down as well is we're going to embed that in the signed URL so whatever the user says these values are that's the values it has to be when it gets to the S3 bucket uh we do this by saying the content type has to be that the content length is going to be that size uh and then we're actually going to have the client pass in a check sum as well which will make sure that the file arrives at the S3 bucket in the same way that it left to the client and we'll use sha256 for this and this is all stuff that gets embedded in the URL that the client then uses so this URL is built securely on the server then sent to the client and used from the client uh we can also embed some metadata if we want to in here which can be handy if we want to be able to associate some data with the image in S3 later on so I'm going to put in the user ID I have from nextor so that if I have code that checks images later on I will know which user uploaded just based on the file stored in S3 so now I need the client to actually send this dat accurately to the server so when we call this function let's see get signed URL we need type size and check sum so the first two are going to be really easy the type is the file. type the size is file. size and then we need to generate a check sum which is a sha 256 check Su so I have a function already that I'm just going to paste in here that does it using the browser's crypto Library uh so I can just set this equal to that and we're going to Hash the file turn that into a string and send that up to the server and this just validates that by the time it gets to S3 that nothing bad happened on the way so let's go why is it complaining there check some is a string I think I need to probably await this yep okay so now the signed URL will have a information in it and this should still work the same way I want it to if I post it should successfully upload that yep but if one of these details was not quite right let's say that I say the size is off by a bite uh if I try and upload this now S3 won't accept it because oh it did it worked oh no it didn't I just don't have good user messages but I can see here yeah there we go it's 43 forbidden because the size didn't match so that is a nice way of making sure that we have some control over which files can actually be sent to S3 so if I do this again it should be working again it sends it to S3 that's all okay so I've done this a few times now and if I open up the S3 bucket again uh I can see that there's still only one file and if I refresh there is just that one test file and that's because this object key over here in my actions test file is the unique identifier for the file so every single time I upload an image or video Whatever to S3 if it has the same name as something that already exists there it's just going to overwrite it it's going to update it so instead I really need a unique name here every single time I upload a file because I never want to have a collision between multiple users uploading files so I need it to be unique but also when we get the URL for these files these publicly sharable URLs you can see that the name is the last thing in the URL so I need it to be unique but I also need it to be unguessable because I don't want people to just be able to come to these URLs and guess what a file name might be like if I used IDs or U IDs people might actually be able to guess that so we need to create a random and uable name here so that we don't get collisions but we also have sharable URLs that are still private enough because they're completely unguessable and for this we have two options if we're using a normal function then we're going to import the node crypto Library if you're using an edge function you don't have access to this and it's just a slightly different function I'll show you in a second but for the node environment we can have something like this to generate a 32 by random string and this is a function that I'm just going to call here every single time I create a new signed URL and if this were an edge function instead of doing these two lines uh you would create a function like this this and all of this code Works in an edge function I'm just using a node environment right now so I'm going to stick with this function and now if I go to upload this image let's go back to the app and I'm going to click post again uh I'm going to wait for this to be successful there we go if I go back to the bucket refresh there's my new image and if I post again I should get a brand new image here because it's all based on name and I did give it two unique names there and just to verify my codes were working I'm going to upload a video file I think this video is 8.9 megab so this is still within the threshold that this should work to get uploaded it just might take a little bit longer as it's a bigger file so that's created now and again I should have another file up here so all of this uploading is working correctly and that's great I have all these files in S3 but I need to associate these with entities in my application so my app has users and posts in the database and the posts are these things that I'm creating here and I want to be able to associate these files in S3 with the user that created them and the post that it belongs to so I also have this media table here and every single time I create something in S3 I want to create a new entry in this table that has any data I need about the item it's going to have the URL the type whether it's an image or a video and then I'm going be able to use the primary key on this table to link it up to a post and then I'll be able to view those posts in my application so so as I'm doing all of this in the back end generating this signed URL I also want to create entries in my database to keep track of all of this and there's going to be a couple of different steps here that will all come together at the end so just bear with me but first after creating this signed URL I'm going to immediately create an entry in my media table and I'm using drizzle so I just have to import all my database stuff import my media from my media table table and then I should be able to db. insert into my media table I want to attach the user ID which I have access to from my session uh the type which is actually going to be either image or video um so I need to say I guess if type starts with image then it's image otherwise it's video that is going to be good enough for now but this is one of those lines of code that ends up causing hours of headaches down the road when something changes in the database so um not a good long-term plan but works fine for now um what else does a media type need so I need uh a type a URL and a user ID okay type uh a URL so the URL here is going to be the signed URL but it's only the first part of the signed URL it's everything before the query prams there we go and why doesn't it like that let's see expected one arguments but got two oh right this is not how drizzle works huh uh let's see insert that values there we go all right and then I'm also going to get the return value from this so just the return object from the database the media object and let's see media result um this is always going to return a collection so I'm just going to make sure I get the first item out of the collection okay so I should now have an item out of this that will have the ID that the reason I did this actually I really could just say returning id media. id so I just want this to return the primary key of the entry that was put into the media table because I'm going to send this back to the client so let's go media ID is media. ID and I need to await this okay now that's good all right so essentially all I'm doing now is creating an entry in one of my tables in the database as I'm generating the signed URL and and uploading that image to S3 and this will give me access to that entry on the client too so I've got the signed URL here also going to change this so I get the URL and I get the media ID and then I'm going to console log this just so we can see that this is all working go back to the application let's submit This Again the video is still on Loop going to go to the console we're going to hit post and there is the ID that was just created so if I go back to my tables I should be able to click on media and there it is there's the entry that was just created it has a primary key one and here's the URL and if I open that in the browser it should show me the video that was uploaded there's that unique identifier and I'm now able to access that in my own database and I have a primary key that I can link to other tables and now that's working I want to actually create a post entity because I have this content here I have the media entry I now need a post in my database that represents the whole thing so when all of this has finished when this is all successful I now want to create a post so this is going to be a new server action I'll just put this down here uh export async function create post and this is going to accept two things so it's going to accept the content that string that I can type in so I'll call that content uh put that as a string that's not how you do types in typescript get that content and uh the media ID just one for now uh let's see can it do this for me type create post ugs fine perfect okay so now I want to call this from the client once that's all successful so we'll await create post I need to import this and we'll send content in an object content media ID perfect okay so now the server should have enough information to create a new post entry and again I need to do a couple of checks so the first thing I'm going to do make sure the user's actually logged in and I'm getting this media ID passed in from the client and I want to make sure that first it actually exists in the database the row was actually created but also that that row was created by the current user so I'm just going to do a quick check here media item equals db. select from media not quite where media. ID is equal the media ID I just need to bring in the equals function and this isn't going to work because media is optional I think uh so again this is going to be something that I only do if the user is currently up uploading an image because Amo allow users to upload posts without the image or video or whatever file um so we're going to do this if they actually try to upload a file um and I'm going to do two where here so where it's equal to that but we'll throw in an end need to import that and this is going to check that the media. user ID is actually the users's ID because if nothing comes back here if there is no item in the database with that ID that belongs to this user then something either went very wrong on our end or we have some sort of malicious user that's trying to attach someone else's media to their own post so either way I'm just going to stop right there abort uh we're just going to say this a failure case maybe media not found um console log or console error some stuff some stuff figure that out later uh but yeah that point just not going to continue however if if we're all good then actually I need to just await this call so if that's good then we need to create the new post so db. insert into the post table which I need to import post from and this is posts and we're going to make a new entry so insert into the post table values I don't remember what is in my post table let's see uh we're going to need need a user ID uh content okay that's it user ID and content user ID content cool okay and this is values perfect and then we're going to get this post item by making sure that this query returns that item and that we only get the first thing out of it cool okay so we create that post item and then I need to update the database because that media item that I have uh contains a spot for a post ID so to associate this video or image or whatever it is with my post I'm going to insert the post ID forign key in this table entry so now I need to do an update down here db. update media and yeah it pretty much seems to have that right except I didn't await this and again this update only happens if someone actually tried to submit this with a media ID and I'm missing that there okay so this should be good now so when we go to create a post we pass in the media ID make sure that actually exists create the post item if there's a media ID associate that with the post item and we're not actually guaranteeing that the file was uploaded to S3 successfully we could put another check in here maybe we um make a request to S3 to double check it good or we could even have the S3 bucket trigger a Lambda function in AWS that then communicates to our database that everything was successful it's kind of up to you and your use cases of your app in this case I'm good with just the client side application informing the server that everything was successful so go ahead create that entry we are making sure that the media item does exist in the database and does belong to this user um and that's good enough for me and then at this point if all of this was successful I don't want to return anything I want to send the user back to the homepage because that's their feed and then they should be able to see the new post so I'm going to import two things up here I'm going to import uh redirect from next navigation and I'm going to import revalidate path from next cach perfect okay so we'll revalidate the homepage just so next knows to actually go grab any new new data there and we'll redirect to the homepage perfect okay so hopefully this is good now let's just look through these steps so when we try and submit the form we first try and get a signed URL then we send the file to S3 just as long as there was a file in that request uh we are going to make the post no matter what so I think I need to refactor this code a little bit cuz whether I have a file or not I still want to create a post if it's just that if there's a file I have this media ID so what I might do here is say that there's a media ID um and it's type is going to be string or undefined no it's going to be number or undefined so if we go through all these steps and get down here I guess what I actually want here is to say that the media ID is equal to that uh and then I get to use it outside here okay so try and upload the image if that's successful create a new post and then that that should redirect us back to the homepage if there were errors though we should handle those and I think this should work let's test it out so I'm going to go back to the app um I'm going to just create a new one here with an image so say uh don't use or and try and post that uploading to S3 once this is done hopefully it takes us to the homepage and it's created and it didn't take us back to the homepage so I'm going to see what was actually created here I have two entries in my media okay that makes sense I have none in my post that's kind of weird I might have messed up my code that that's a likely scenario here so let's see if we're getting any result here um and I'll console log that out because this is how the error cases are handled so I really should be checking to see if there's an error there let's post again and failure media not found okay that's kind of weird so the media ID I wonder what that is uh let's check it media ID let's post that so the media ID is five that's coming out just fine and when we go up here that would be an entry in the database oh okay that was really stupid I didn't check all I need to do was check okay so if there is no media item so cons media item equals the error is only if there isn't an item okay I was not paying attention to what I was actually writing there okay all right so it was always erroring out okay try it again uh let's see this should hopefully work we get that okay finally and it redirected back to the homepage where we should actually see the image appear I've already set this page up to work so I must have missed something else I am not actually awaiting this so when I go and update the foreign key it's not happening all right let's try that again one more time North all right uploading once this is done o oh no good yay all right awesome so now it's trying to present the image from S3 on the homepage we're getting this error because I haven't actually set this URL up to be accepted by my application so we go to the next config JS and add this as one of the domains so this is going to be my threads clone local Sim all the way up to. perfect okay so now see hopefully this should work and there we go the post are showing up and I'd already set this page up to work with this schema and we can take a look at the queries over here here so every time it goes to the homepage it's selecting the posts user and media from those tables um and it's just joining those all together to actually get that image we should even be able to see yeah okay so when we go to create a new post let's actually do this let's create a little space and see what queries are happening when I go to create that post kind of review that process so go in here um let's select an image let's select a longer video here uh um here's a video and then we're going to post this and look at the queries that come out so first we're inserting into the media table and we're putting in all of that information it makes its way up to the S3 bucket at that point we then that happened a lot um select from the media table to grab that item make sure it exists and belongs to that user then we create a new post item then we update the media table to link link it to that post item and then we get redirected back to the homepage where it selects and joins all of those together and there it's it's all working and because these are public URLs I've set it that uh I can obviously play the video but if I click on the video or the image here it will open up the actual URL so this is now a sharable link if I want to share it but it's unguessable if I don't want to share it so now we're going to look at just one more thing and that's being able to delete these posts cuz right now I have these delete buttons but they don't currently do anything and when I do delete a post it is going to delete the post from the database it's going to delete the media item from the database but it also needs to head over to S3 and delete the item from S3 so it's a little bit of a process here so if I go back in here and I'm going to go to my home feed now uh I have this action already set up so it is console logging delete with the post ID every single time I hit the delete button that's already been connected I can see those deletes happening so all I really need to do is is within this server action perform all of those things delete from the database and delete from S3 so the first things are going to be pretty simple uh well yeah I guess I do need or I need to make sure that the user is currently logged in um and I would also want to make sure that the user owns this post that they're trying to delete so let's see can I grab the post from the database well I need to import the database actually I'm just going to go to my other action and steal all of this because I feel like it's all going to be relevant so yep all right so we grab the post if there is no post then you know something bad happened they're trying to delete something that doesn't belong to them or something went wrong on our server so we're just going to send a failure message um otherwise we can delete the post we can delete the media perfect and we need to delete from S3 you might want to do this in a transaction but I'm just going to leave this as is so to delete from S3 I'm going to go over here and the code is going to be kind of similar for a lot of this stuff so setting up the S3 client is going to be similar I'm going to copy and paste all this uh CU I want this S3 CL I don't need any of this stuff I don't need any of this stuff but I want the S3 client and we're not going to use a put object command we're going to use a delete object command uh so I have my S3 object then down here we're going to create a new delete object command which needs the bucket name and the file name the the key there so this actually is inside of the media table so as I delete that I am going to get the media item uh just make sure that's returning and I only want the first item there we go so this should be media item. URL uh but it's actually just the last part of the URL is the name so I'm going to split that okay there we go that looks good all right so this is the key that I want to delete this is the command and I don't need to generate assigned URL I don't need the client to perform this task because we're not actually grabbing this large file we're not going to process the file in any way we just need to send a message to S3 saying hey delete this file so this is something that we can just execute right here from the server action so we can call and actually that looks exactly right we look through these steps again make sure the user is logged in make sure the post belongs to the user uh Delete the post delete the media item delete the file from the S3 bucket so now let's see uh I don't know which one this will be let's just look at the number of objects So currently in S3 there are 12 objects uh and currently in my database I have three posts so if I go and delete this post and everything goes well I'm going to check the network tab too if I hit delete it should oh that failed build horribly let's see if there's something in the console um it's saying that I'm violating a foreign key constraint so it might be just that I need to delete my media item before I delete the post okay media item has a foreign key to the post so delete this then delete this uh okay let's see don't know why that was so bad all right let's hit delete Local Host okay fine did the post okay it didn't actually try deleting from S3 I want oh right yeah because the S3 connection is happening from the server I'm not going to see this in my network logs here but everything seems to be fine um I didn't actually call a revalidate here so I should do that as well let's import revalidate path because once I've deleted I'm going to refresh the homepage okay so that seems to be deleted I did have 12 objects in here now I should have 11 that's great uh I did have three objects in here should now have two Perfect all right now let's try that again and see if it just works seamlessly so we hit delete it goes away here we should go from 11 to 10 now perfect and in my database I now have one post left and that's it for this video but make sure you check the description for any links and code examples if you have any questions feel free to leave a comment and don't forget to like the video and subscribe to the channel
Info
Channel: Sam Meech-Ward
Views: 8,705
Rating: undefined out of 5
Keywords: next js, next js 14 tutorial, next js project, server actions, vercel blob
Id: t-lhgq7Nfpc
Channel Id: undefined
Length: 49min 26sec (2966 seconds)
Published: Tue Nov 14 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.