Create a Task System with Unread Filtering and User Assignment in Hasura

Jason Brown
InstructorJason Brown
Share this video with your friends

Social Share Links

Send Tweet

We'll create a task system with permissions, unread task filtering and assigned task filtering all without a single line of code. Walking through the Hasura console creating the tables, relationships, and finally leveraging Hasura and its role based access control. We'll finish by showing off the GraphQL queries and mutations that you would use in your application.

Jason Brown: [0:00] To get started, first install the Hasura CLI from Hasura, as well as make sure that you have Docker installed and that Docker is running. Once Hasura is installed, we can run the Hasura CLI to init a project. We'll say hasura init, and we'll specify an --admin-secret, say myadminsecretkey, specify the directory that we'll install into, and it will just be hasura.

[0:27] Now we have the boilerplate for a Hasura project set up. Additionally, we want to pull the docker-compose down from the provided manifest from Hasura. You can see here that it sets up a database, and then it sets up a graphql-engine from Hasura and exposes on port 8080. Then we specify our admin-secret key here, which is the same one that we initialized our project with.

[0:50] With these two things set up, we'll run docker-compose up -d, where the docker-compose is Yaml's at, and this will initialize. Then, we'll go into the Hasura directory and run hasura console. This will open up a Hasura console in the browser, and we can start making edits to our tasks.

[1:14] The first thing we want to do is set up our data. Our data will be a series of tables that will be saved to Migrations. First off, we'll set up our users table, so users. We'll use the Frequently used columns and select the id for UUID. Then, we'll give a name column, which is going to be a Text column.

[1:36] With that created, now we want to create our tasks. We'll click Add Table, set up our tasks. Again, we'll do a UUID. We'll use the created_at. We'll also add the updated_at. We'll give it a title of Text. Then, we'll give it a due date, which will be a Timestamp. Go ahead and click Create.

[2:03] We should additionally add a created_by, which you can always come back and add anything after the fact. We'll go say created_by, and then we'll specify a UUID and click Save.

[2:21] Two more tables to go. We'll first add another task_assignments. The reason we're doing it like this is that we will be able to assign multiple people to a particular task or just one if we wanted. We'll select Big Int, because we don't care that there's a crazy big unique identifier that we're going to display to people. We'll put task_id here, which is a UUID. Then we'll put a user_id, which is a UUID.

[2:54] Finally, we'll add a tasks_read table, which will keep track of whether or not a person has read a particular task. We'll do this with a task_id as a UUID, the user_id, a UUID. Then we'll also do a created_at so we know when the person read the task.

[3:20] With all this created, we can now start adding our relationships to the data. First off, we'll set up our tasks and we'll set up Foreign Keys. There's two options, but we'll go with Foreign Keys, because then Hasura will automatically know what is assigned and associated with what. We'll say the users is the user id or the created_by to an id, and we'll go ahead and save that.

[3:48] Next up, we'll assign the tasks_read, Foreign Keys for that, and we'll say the tasks table is related to the task_id and save that. Then we'll add another Foreign Key to the users table. Then we'll do our task_assignments. We'll add our Foreign Key to the tasks, task_id to the id, and then the users to the user_id.

[4:28] With all this set up, once we go back to our DATA tab, Hasura will see that there's all these Foreign Key relationships, and we can now track them specifically, or you can click Track All to track all of them.

[4:51] When you track a table, it allows you to then query for things and then query those nested datas. Rather than searching for tasks and having to query then for task_assignments with a separate query, we could now grab task_assignments as subdata of every single task as well as the tasks_read.

[5:13] Now we finally need to set up the permissions. The permissions will allow a user to make actions based upon their role. First, we'll go to our Permissions for our users and we'll first add another role called user. The user will be able to select with a custom check where the id of this particular user is equal to the X-Hasura-User-Id. This is the id that is exposed inside of a token.

[5:44] These values are all pulled from the Hasura claims inside of the token that allow you to do and specify the different CRUD operations to that particular role based upon the claims inside of the token. We want to allow people to assign tasks to other people. We're going to say that they can select the id and name of a person without any permission checks.

[6:07] For the tasks_read, we'll just say that a person can select where the user_id is equal to their User-Id. You'll only be able to see where you've read a particular task. Additionally, with the same here, you'll be able to insert.

[6:31] One additional thing is we don't want to be able to allow a person to insert any user id or need to insert a User-Id to claim that they've read a task. What we can do is we can set the Column presets to user_id from a session variable and say User-Id. Then we could remove this permission check and say all users can insert for a specific task.

[6:56] We don't want them inserting an id or created_at. Those are created automatically. All they have to do is insert a task_id, and the User-Id will automatically be inserted from their token.

[7:11] Then, for task creation, we'll set up permissions. Anybody can query for tasks. Anybody can insert a task. However, they'll only be able to insert the title and a potential due date. Then again, the created_by can be pulled from a session variable.

[7:39] Finally, the assignments. For our assignments, we can say that you can insert with a custom check or without any custom checks. You can insert for a task_id or anybody's user_id. Additionally, you can select all assignments.

[7:58] Potentially, you only want it to select for yourself. You would then say the user_id is equal to the X-Hasura-User-Id. Only people would be able to see once that are claimed for themselves. If you wanted to see things claimed for other people or anything, you would then just remove this particular insert check, or select check, I should say.

[8:22] With all of our select and insert permission set up, we can now start building out our queries. First, we need to insert some users. If we go to our users table, we can do that from here. We can say Jason Brown. We can insert another person. Now we have two users that we can work with.

[8:54] If I copy this particular user id, I can actually pretend like I'm an authenticated user, and for that same role, as long as I supply the admin-secret. If I say x-hasura-role, I can say I'm a user. Now that immediately limits what I can and cannot see. If I say x-hasura-user-id, when I do any actions, I'll be operating as this specific user.

[9:19] If we want to create a mutation query where we insert a task, we can do so. You can see that we can only insert the two things that we selected. We would say title: "This is a task." For anything that is date-time related, we can always specify now() inside of Hasura, and we'll just return the affected rows.

[9:47] We've now inserted a singular task. If we go here, we can see that task has been inserted, and it was created by the person I was pretending to be.

[9:56] One really cool aspect of Hasura is that because we've created these relationships, if I want to insert a task and assign all at the same time, I can do that. I can say insert a task where the user_id is equal to this other person. We don't need to worry about the task_id because that will be automatically filled in for us.

[10:23] If I create a second task and say, "This is an assigned task," insert the two values. This is an assigned task. If we go to the task_assignments, we can see that the task_id and the user_id is the person we said as well as that particular thing, and this is all done within a single transaction.

[10:52] We might want to query for things that we're assigned to or things that we have not read. For example, if you want to query for all the tasks that you are assigned to or not assigned to, we'll grab our Hasura-User-Id and we'll say get us all the tasks where not tasks_reads user_id equals our User-Id. This will get us all the tasks that I have not inserted a User-Id into the tasks_reads table.

[11:28] If I run my query, it returns the two tasks. However, if I were to then insert a mutation here and say insert_tasks_read_one for the task_id here, we'll remove this portion of the query, so we don't insert another one and we run our mutation.

[11:55] If we rerun our top query, we only get the one task that we were not inserting as being read. If you look in our tasks_read, you can see that we have read that particular task. The same thing can be done for tasks that you have read. Rather than not, you can say get me all the tasks that I have read, and now it swaps to the tasks that I have read.

[12:28] Then again, if you don't supply a where clause or you supply an empty where clause and query, it'll just return all the tasks.