Delete an Appwrite Document from a Database Collection in React

Colby Fayock
InstructorColby Fayock

Share this video with your friends

Send Tweet
Published 6 months ago
Updated 5 months ago

We have a list of some pretty amazing events, but two of your event managers just submitted the same event twice! :gasp: Does that mean they have to manually edit the Document’s fields? Likely not… we don’t want people having to manually edit a database. Instead, we can give the option to delete events, so that whether it's a typo or an invalid event, we can quickly get rid of it.

As we can probably expect at this point, the Appwrite SDK not only gives us the flexibility to create, but also delete any Document or File we want. So that means, we can easily clean things up by passing in the ID of the Document or File we want and it's gone! The tricky part though, is how do we make sure we always delete the File associated with the Document so we don’t end up with a bunch of extra unnecessary files getting stored.

In this lesson, we’ll first learn how we can use the Delete Document endpoint to delete any of our Database Collection entries. As we know, there’s a field within that Document that references a file, so we’ll see how we can first use that reference ID to delete the associated File, then continue on with the Document deletion process. And to do all of that, we’ll make sure we have the right permissions configured to avoid making unauthenticated requests!

What You’ll Learn

  • Delete a Document from a Database Collection by ID
  • Delete a File referenced in a Document by ID
  • Configure Delete permissions for Documents and Files

Resources

Instructor: [0:00] Up until this point, we've been able to create all the events we want with no rules, but now it's time to start to delete some of that data. How can we actually give that capability?

[0:08] Now that we have the full capabilities to add an event, such as including this image with uploading files, we want to be able to delete all the old events and then just start fresh and add all of our full event data.

[0:19] Now, remember, not only do we have all this event document data, each of these documents will include an image file ID that references a storage item where inside of the bucket we can see the associated files for those events.

[0:33] One thing to point out is it might be a legitimate strategy to retain all those files, those image files, long term, rather than deleting them along with the events.

[0:41] Where maybe you've had that event up for a while and maybe somebody's using that image link, whether you want them to or not, and you don't want to necessarily break that image link.

[0:49] It's going to be up to you as to whether you want to actually delete those files along with the event data, or if you want to leave those long term.

[0:55] Heading back to the Appwrite documentation, where we start to look at the different APIs we have available, each of the different services are going to include a delete endpoint, where it's going to give us the ability to pass in all of our different identifiers and delete that piece of data.

[1:09] Not only do we have that available for our database, but also for the storage API, where we can use the storage delete file endpoint in order to delete the file associated with the event.

[1:18] Looking at the events that we currently have available, we have a lot of events without an image, and then we just have one with an image. Let's start off with some of the ones that we just have the event data to get started.

[1:28] Inside of the database API, where we'll want to look at the delete document endpoint, we can see that the way that this is going to work is we're going to use the delete document method, where we'll pass in the database ID, the collection ID, and finally the document ID.

[1:41] As we usually do, let's head over to our lib directory, where inside we'll go to events, where we can start off by cloning one of our existing functions, such as getEvent by ID, where instead we want to delete an event by the ID.

[1:54] Now, for the most part, this function is going to be pretty much the same, where we're going to pass in a lot of the same information, including that database ID, the collection ID, and then finally the event ID. The main difference, of course, is we don't want to call getDocument, we want to call delete document.

[2:09] At this point, we can already see that TypeScript is yelling at us, we don't really have a document to respond with. We can just turn this into data or results or whatever you want to do, and we can return that object. From the function.

[2:22] Technically, this function is pretty much done, but we could probably do a little bit of an optimization on the TypeScript front, where this is going to be a string, but technically, we know it's going to be a Livebeat event ID. What if instead of a string, we call this a Livebeat event, and then reference our dollar sign ID.

[2:40] Again, at this point, this really shouldn't be anything noticeable throughout our actual work. If in the future, for whatever reason, this string value changes on this ID, we know that it will automatically pull in that updated type reference.

[2:53] With that in mind, we can even copy that and apply it to our other functions like getEvent by ID where we can replace the string with that ID reference. I think for now, we're good to go with our delete function. Let's start to use it.

[3:05] We're going to head back over into pages/event/event ID, where if we scroll down all the way close to the bottom of this file, we can see that we have this commented out button. If we uncomment it out, we can see that it's going to simply be a delete event button using this button component.

[3:19] The other thing that we want to make sure that we do is head back up to the top of the file and uncomment out that button import. If we head back to the individual event page, we can see that we have this nice delete event button that's going to allow us to trigger our functionality.

[3:32] Now, before we dig in, this button component is really just a wrapper around the button element, which allows us to just have some consistent styling. So that means on our button, what we're going to want to do is we're going to define an onClick handler, where we're going to want to call it something along the lines of handle on delete event.

[3:49] At the top of our page component, we can define this somewhere, remember to say function handle on the events where whenever this is triggered, I want to fire that delete event function. In addition to our get event by ID on lib events, let's also grab our delete event by ID.

[4:05] Back inside of our function, let's run our delete event by ID, where we know we're going to want to await this function. We also want to make sure that we make this function async so that we can use that await syntax in the first place.

[4:19] Then as we know, we also need to pass in our event ID as our argument. Now we have two options for where we grab this value where we have our params where we can grab our event ID, which comes from the router, or we can use our state, which includes the full event object. Now either probably would be fine.

[4:36] If we use the event object, we know that we're getting that data from the actual server response. We know that that event ID actually exists. As my argument, I'm going to use event then dollar sign ID. Of course, there is that possibility that the event state hasn't been updated with that server information yet.

[4:54] We just want to make sure that we say if this event ID does not exist, we want to return before we try to actually run that function. Finally, once this event is deleted, we probably don't want to stick around on that individual event page.

[5:08] Like we learned when we were creating a new event, where upon the successful creation of event, we navigated to that event page.

[5:15] Once this is complete, we can just navigate back to the home page. We can import our useLocation from Wouter, which if you remember, Wouter is the router solution we're using in this project, where we can grab our navigate function, which will be the second index of the array returned from our useLocation hook.

[5:35] Then after that successful deletion, we can then run navigate, where we can simply pass in a slash, which will take us to the home page. Let's give this a shot. Let's start off by trying to delete eats and beats. Once we click Delete event, it looks like, uh-oh, we hit one of those pesky permission errors again.

[5:51] Heading back to the Appwrite dashboard where we head over to our database collection and go to settings and we scroll down, we can see the permissions that we currently have set. If we remember, we only set read access and create access for anyone who accesses the app.

[6:06] Just as a reminder, we'll be configuring more granular permissions later in this course, but for now, we don't have any kind of authentication method, and we want to get this working.

[6:16] What we can do is we can check the delete option and hit update where now if we try to click Delete event again, we can see that it was successfully deleted and we are taken back to our home page. We can also see that we no longer see that eats and beats events meeting, it was successfully deleted.

[6:31] Again, make sure you're cautious with deploying this application before configuring those advanced permissions, which we'll do later in the course. Before we go through and delete all these items that don't have an image associated with them, let's make sure that we can test actually deleting an item that has an image.

[6:46] Let's head over to Kolbe Test 4, where once I hit this delete button, not only do I want to delete that database document, I want to also delete that storage item. If we look inside of the storage API, we can find the delete file endpoint. We're here, we're going to want to run the delete file method on the storage service where we're going to pass in the bucket ID and the file ID.

[7:06] Heading back to the project where I want to navigate to lib-storage, I now want to delete a file. I'm going to go ahead and start off like I usually do by cloning one of my existing functions, where instead of uploading a file, I want to delete a file. I'm going to do that by its ID.

[7:22] Now looking at the argument, we're not going to be passing in the entire file, we're going to just be passing in the ID of the file. Let's change this file to file ID. That means, we need to also update the existing type. Unlike Livebeat event, we never created something along the lines of Livebeat file.

[7:38] For now, I'm going to go ahead and replace file with a string, but on your own time, try to see if you can create a new Livebeat file type, where we would have that ID that we can use as a reference for both deleting a file by its ID, as well as getting a preview image by its ID.

[7:55] With our new file ID parameter, we need to make sure that we're passing it to the right method. On our storage service, we're not going to be creating a file, we want to now delete a file. Looking at the arguments that we're passing into this, we're currently passing in our bucket ID, a unique ID and the file.

[8:11] As we saw in the documentation, we only have two arguments where we have the bucket ID, and then the file ID, so we can get rid of these final two arguments and replace that with our file ID. With a few updates, we now have our delete file ID function, where we can start to use it inside of our project.

[8:28] As far as using it, we have two options available to us, where if we start by looking at our handle on Delete event function inside of our event page, if we want, we can simply create a new line where we delete our file by ID. Given the scope of our project, that would probably be fine, as we're only really doing this in one place right now.

[8:45] If we start to think about how this would work in a bigger project, or as we start to use that endpoint in more locations, we need to make sure that every single time we Delete event by ID, we also delete that file, otherwise, we might start to have additional files and lose track of those references.

[9:01] Another option would be heading over to our lib events file, where if we go to our Delete event by ID function, we can couple our file deletion inside of our whole Delete event function. This does come with a tradeoff, though, because when we run this Delete event by ID function, we're only accepting the event ID.

[9:19] While technically, we could probably update this to accept whatever arguments we want, including that file ID is somewhat defeats the purpose of the simplicity of being able to Delete event by only its ID, including anything associated with it. Another option is to first get the event itself.

[9:34] Using our existing Get Event by ID, we can first say constant event is=await Get Event by ID, where we're going to pass in that event ID, and we know that this is going to return an object where we want to destructure our event. Then in addition to deleting our document, we can also delete that file. I'm going to import my delete file by ID function from at lib-storage.

[9:59] Once I have that event data before I delete the document itself, I'm going to run constant results file = await delete file by ID, where then with the event, I can pass in event image file ID.

[10:14] As we can see, TypeScript isn't happy because, first of all, image file ID might not even be on the event itself. As we know, it's an optional field. I'm going to say if we have our event image file ID, only then will I actually run this delete file by ID function.

[10:29] Now, as far as the results file goes, we're currently not doing anything with any of our results. If we look at the endpoint, it's not going to return any content anyways. Really, we could probably just get rid of it.

[10:39] I'm going to just do that. I'm going to get rid of the results file as well as the other results that we're currently passing back. Now for our final delete event by ID function, we're going to first get the event by its ID to make sure that we have all the image information we need. We're going to then try to delete that file if it exists.

[10:56] Then we'll finally delete the actual document itself. Now, let's actually give this a shot. I'm going to go ahead and delete the event. Yet again, we're hit with that permission error. Now before I even head over to Appwrite, you probably already know what this is.

[11:08] If we head over to our storage and images bucket, and then we go to our settings and then scroll down to the permissions. Of course, we never added the delete permission for our storage bucket. Let's go ahead and update that to delete. Of course, I need to remind you that we will cover more granular permissions later in this project.

[11:27] Now if I go ahead and delete the event, we can see that it was a success. We can even see at the top level of our bucket that we now only have one file where this file was just a stray that we had from the beginning of trying to upload files. We can go ahead and just clear that out or any of the other files that we have in there that aren't related to an existing event.

[11:44] Now before we move on, it's also worth mentioning that we could also use functions in order to trigger a deletion of a file anytime one of those documents are deleted. Or when defining that function, we can set up exactly what event we want it to trigger on where in this case, we would want to select it to happen whenever a delete occurs on that document.

[12:03] Now we can start to think about going through and cleaning up all these events so that we can go ahead and add all of our full events, including our image references. I know you're thinking it because I'm thinking it. Our application isn't secure because literally anybody can go inside the application.

[12:17] They can create an event. They can add files. They can even delete the entire event and we don't have any way to prevent that. Next, we're going to dig into authentication where we're going to add login to our application so later we can lock things down so that only the people we want to perform certain actions can do so.