Task systems can be tricky to build, but with Hasura's permission system, subscriptions, and event triggers you can spin one up in an afternoon. You'll have real time tasks, read, unread, as well as permissions for reading, and creating tasks.
We'll walk through what the database structure will look like, building the queries, mutations, and permissions for the task system.
Ensure you have docker
and the hasura
cli installed. You can find the documentation for installing the Hasura CLI here
Due to Hasura and it's super flexible querying system we are able to accomplish many common filtering requests that would apply to a task system without using any custom code. This task system could also be used as a notification system or really anything
To setup your hasura project run the command below. This will create the necessary folder, with all the empty metadata necessary.
hasura init --admin-secret myadminsecretkey
Next grab the docker-compose.yaml
provided by Hasura. You can get it here or copy from the code below.
version: "3.6" services: postgres: image: postgres:12 restart: always volumes: - db_data:/var/lib/postgresql/data environment: POSTGRES_PASSWORD: postgrespassword graphql-engine: image: hasura/graphql-engine:v1.3.3 ports: - "8080:8080" depends_on: - "postgres" restart: always environment: HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres HASURA_GRAPHQL_ENABLE_CONSOLE: "true" HASURA_GRAPHQL_DEV_MODE: "true" HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey volumes: db_data:
Now that you have it saved in the root. Run the commands below to get the hasura console open.
Docker compose will create the DB, and Hasura, then expose hasura on port 8080
. Running hasura console
will open up a browser at http://localhost:9695/
this will allow us to make changes and have them saved to the migrations
and metadata
so you'll be able to apply the stuff to your production environment.
docker-compose up -d cd hasura hausra console
We'll flush out what our tables will look like, for the sake of this article we won't make a real users table, or login that will be left up to you.
# users - id - name
# tasks id created_at created_by title due
# tasks_assignments id task_id user_id
# tasks_read id task_id user_id created_at
Once your console is open visit the Data
tab.
Click Create Table
.
Then start filling out the tables.
This portion is easier to watch in the video.
There are 2 options. You can setup foreign key relationships or just relationships between fields inside of Hasura. I lean a little bit towards foreign key relationships to ensure data integrity but foreign keys can be limiting.
Some of the power ofs of relationships with in Hasura is a field could have a mix of values, and point to other tables from a singular column.
For example: If the user_id was a UUID it could point to an internal user. If then you wanted to support exteranl users you could allow for email
to be inserted into the same column.
This tutorial will leverage foreign key relationships.
Hasura permissions can be tricky to explain via text. I recommend watching the video to see how all of the permissions get setup.
Before getting started insert a few users so that we can work with some user ids. These are required because we setup foreign key relationships. This means that if we insert a value it must appear in the other table.
We aren't running an authentication system so we must set our values in the header. This is allowed because we are passing in the admin secret. This allows us to pretend to be any role, and any user.
One such query you might run is querying for all the tasks. This will load all task info, who it was created by, who has read it and also who it is assigned to.
query LoadAllTasks { tasks { id due created_at user { id name } tasks_reads { user { name id } } task_assignments { user { id name } } } }
There aren't any variables here for the graphql query like there would be a normal insert. I am showing that Hasura allows you to do a creation of a specific thing and if the lower tables have a relationship to the primary key it will be automatically inserted.
So we can insert a task, and assign it to a specific user and when the task_assignment
is created it will be pointing to the newly created task in the task_id
column. This is all done in a transaction so if something breaks we won't have a task, or any assignment created.
mutation InserTask { insert_tasks_one( object: { title: "Test Task" due: "now()" task_assignments: { data: { user_id: "3938e4f3-25cd-4410-b702-59fab96790e0" } } } ) { id due title user { id name } } }
Some other general queries might be querying for unread tasks. We can use Hasuras _not
query to find where values don't exist. In our case we are querying for where tasks don't have a task_read
user equally a specific user_id
. You could query for tasks about yourself, or even tasks that another person has not read.
query GetUnreadTasks($userId: uuid!) { tasks(where: { _not: { tasks_reads: { user_id: { _eq: $userId } } } }) { id due created_by created_at title updated_at user { id name } } }
The opposite then is also the case, querying for tasks that you have read. We remove the _not
.
query GetReadTasks($userId: uuid!) { tasks(where: { tasks_reads: { user_id: { _eq: $userId } } }) { id due created_by created_at title updated_at user { id name } } }
To insert a task read because we have our column preset we only need to supply the task_id
and the user_id
will be pulled from our hasura claims and inserted in so that we aren't allowing users to say that another user read a task.
mutation InsertTaskRead($task_id: uuid!) { insert_tasks_read_one(object: { task_id: $task_id }) { id } }
The same queries for task read can also be done for task_assignments
, or search for things on specific due dates, etc.
The next step after this would to be leverage Hasura's event trigger system so that when a task is created, or a task is assigned to you a user they can be notified. Event triggers fired when you select to be notified for inserts, updates, and even deletions.
This would allow you to track if a user needs to be notified they were assigned, or that they were unassigned to any task.
Hasura has allowed us to build a flexible task system that allows for tasks to be assigned to multiple people, and the ability to track who has read tasks all without a single line of backend code.
We were able to leverage the role based access control, and permission system to manage what users can access and automatically track who has created a particular task.