
Join the Conversation!
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
"Please login to view comments"
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
Complete source code for this lesson is available at
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
##Looks like we found a thief monkey By the way, I liked the trick how you reached till here. You have a good sense of humor. You will improve a lot if you join our course with this passion.
var
(function-scoped, outdated)let
(block-scoped, modern and recommended)const
(block-scoped, cannot be reassigned)_
, or $
let let = 5;
is invalid)myVar
and myvar
are different)string
, number
, boolean
, null
, undefined
, bigint
, symbol
Objects
, Arrays
, Functions
Subscribing gives you access to a brief, insightful summary of each lecture to stay on track.
00:00:02 In this module, I'll teach you how to implement collections, or you can call them favorites or saved questions.
00:00:11 Whenever you find a question that you like, or let me rephrase it, when you get a bug and you've been searching for an answer for hours,
00:00:19 sometimes even days and you couldn't find it, but then you finally find that one dev overflow thread where there's an answer that solves your problem.
00:00:28 You definitely want to bookmark that.
00:00:30 So that's exactly what the saved questions or collections or favorites will do.
00:00:34 To implement it, let's create a new action within lib actions, and let's call it collection.action.ts.
00:00:44 Here, we can add an action that adds a question to the collection, which means that we save or bookmark it.
00:00:50 As usual, whenever we create an action, we also have to create a validation schema and its type.
00:00:56 So let's head over into validations.ts.
00:01:00 And right at the bottom, I'll create a new validation for the collection base schema by saying export const collection base schema.
00:01:11 And it'll be equal to z.object where we simply need to get access to the ID of the question we're saving, which is going to be z.string.men1 question ID
00:01:23 is required.
00:01:25 And we'll also create a type, so let's head over into the action.d.ts.
00:01:31 And at the bottom, I'll create a new interface of collection base params.
00:01:38 And we can take in a question ID and make it of a type string.
00:01:43 Finally, within our collection action, we can add a use server directive at the top, since we want this to be executed on the server strictly.
00:01:52 And we can start creating our first server action by saying export async function.
00:01:59 toggle save question and as usual we'll accept the params of a type collection base params and we'll return a promise of an action response specifically
00:02:13 of a type saved boolean.
00:02:16 So here we can define exactly what the action response will contain and it'll contain just a single boolean property whether we have saved it or not.
00:02:25 Next, we can get access to the validation result by calling our special action handler And here we can pass in the params,
00:02:33 which we're passing into the server action, as well as the schema that we want to validate against.
00:02:39 So this will be a collection base schema.
00:02:42 And in this case, we want to set authorized to true because only authorized users can save questions.
00:02:49 After that, as usual, we want to check if validation result is an instance of error.
00:02:56 In that case, we can just return handle error.
00:03:00 and pass in the validation result into it, and define that it'll be in a form of an error response.
00:03:08 And it might seem like I'm going fast through this, but I know that we have already done this many, many times for each and every server action we created before,
00:03:15 so it's the same thing.
00:03:17 After we verify that we have passed all the right params that match the following schema, we can then try to extract some of the values out of it.
00:03:26 So we can say const and destructure the questionId from validationResult.params and we can make an exclamation mark saying that we know that it'll be there.
00:03:38 And since we are dealing with a user, we know that because we have set authorized to be true, we also have access to the user ID coming from validationresult.session?user?id.
00:03:51 Then we can open up a new try and catch block.
00:03:58 In the catch, we can just return handleError as errorResponse.
00:04:05 And in the try, we actually first need to fetch the question that we want to save.
00:04:10 So let me say const question is equal to await question.findById.
00:04:17 And we're going to pass in the question ID.
00:04:20 Then if for whatever reason there is no question, we can just throw a new error and say question not found.
00:04:29 Of course, for this question, we actually have to import it from the database to have access to its model.
00:04:35 Next, we can try to form a collection by saying const collection.
00:04:41 is equal to await collection coming from add forward slash database dot find one.
00:04:49 So we want to see if a collection for that question already exists.
00:04:53 So we need to pass in the question of a question ID, and we need to know not only whether some user has added that specific question to their collection,
00:05:02 but also whether our current user has added it.
00:05:05 So we're going to pass in the author equal to user ID.
00:05:09 If it already has been saved, so if collection is true, then I can just await collection.findById and delete because that means that we want to unsave it.
00:05:23 And we can just return an object where we set success to true as well as the data of saved to false.
00:05:33 But of course, if a collection doesn't exist, In that case, we can await collection.create to which we want to pass over the question set to the question ID,
00:05:45 as well as the author set to the currently logged in user.
00:05:50 And once we save it, don't forget we need to revalidate our path.
00:05:54 So this will be a path of forward slash questions.
00:05:59 So we'll point to the details page of that question.
00:06:04 And we need to import revalidated path from next cache.
00:06:08 And of course, we can have this question details as a part of our dynamic routes right here in the constants.
00:06:15 Let's see if we have question details.
00:06:18 I don't think we do, right?
00:06:19 So right here, I can create a new one, and I think that'll be question underscore details, where we will accept an ID of a question of a type string,
00:06:30 and we will simply return two questions, and then we might as well call it question ID.
00:06:37 So now instead of just manually saying forward slash questions forward slash ID.
00:06:42 Oh, no, sorry, sorry.
00:06:43 It's my bad.
00:06:43 Take a look at this.
00:06:44 We already have it.
00:06:45 It's called a question takes in the ID and points to the question ID.
00:06:49 I just saw it when I started hovering over this.
00:06:52 Typically that then shows you another occurrence of that specific code in your file.
00:06:59 So instead of manually saving it right here, we can just say routes.
00:07:04 coming from constants.question and we need to pass in the question ID.
00:07:10 Finally, once that is done, we can return a success of true and saved of true as well.
00:07:16 And that is it.
00:07:17 This is the code for a server action that toggles on the ability to save or unsave a specific question.
00:07:24 So let me go right here and let me add it.
00:07:28 I'll say add to collection action.
00:07:31 I'll commit it and sync it.
00:07:34 And in the next lesson, we can implement that server action right within the code.