Now that our backend supports user authentication, we can extend it to handle task management, enabling us to replace the mock API on the frontend with the real one.
We are going to split the work into layers:
Our goal is to let users manage tasks. Both users and tasks belong to an organisation, and users within an organisation can access the tasks of the organisation. Task management operations are:
getTasks(filters?, sortBy?, limit?, offset?)
– Retrieves tasks within the organisation, with optional filtering and sorting.getTaskById(_id)
– Fetches a specific task.createTask(taskProps)
– Creates a new task within the organisation.updateTask(_id, updatedFields)
– Updates a specified task with new data.deleteTask(_id)
– Deletes a task.To enable task management, we first need a database schema that defines tasks and their attributes.
We reused the existing Task
schema in @repo/validation-schema
, and extended it with some fields:
_id
: String – the ID of the taskorgId
: String – ID of the organisation the task belongs toname
: String – name of the taskisCompleted
: Boolean – completion indicatorcompletedAt
: Number? – when the task was marked completedupdatedAt
: Number – when the task was last updatedcreatedAt
: Number – when the task was createdWe then created a new Mongoose schema in @repo/mongodb-helpers
based on these types.
Since the Task
type is inferred from the updated task schema, new fields (such as orgId
) were introduced, which are currently unknown to the frontend. This caused a temporary mismatch between the API and frontend types. To address this, we introduced a transitional type, MockTaskType
, to maintain compatibility until the frontend is updated.
The task service is responsible for handling task management – creating, reading, updating, and deleting tasks (CRUD operations).
For the context to pass down to the handlers we will initialise resources:
connection
with the connection string (DB_CONNECTION_STRING
) coming from the config.TaskModel
to perform database operations on the tasks collection.getTaskById
– Finds and returns the task that matches the _id
and orgId
supplied.getTasks
– Retrieves a list of tasks based on filters
, with sorting (sortBy
), pagination (limit
, offset
), and formatted filters.createTask
– Creates a new task using the provided arguments and returns the created task as an object.updateTask
– Updates an existing task by _id
and orgId
, applies the provided update fields, and returns the updated task.deleteTask
– Deletes the task that matches the _id
and orgId
, then returns true
upon success.formatFilters
– formats object based filters into mongoose syntax filtersWe defined the queries, mutations, inputs, and output types in the GraphQL schema:
_id
, orgId
, name
, isCompleted
, completedAt
, updatedAt
, createdAt
.TaskFilters
allows filtering by _id
and name
using StringFilters
.TaskSortField
enum: Supports sorting by name
, createdAt
, updatedAt
.TaskSortBy
input: Specifies sorting field and method (SortMethod
– asc
/desc
).getTasks
: Retrieves a paginated list of tasks with filtering, sorting, and limits.getTaskById
: Fetches a single task by its _id
.createTask
: Creates a new task with required name
.updateTask
: Updates a task’s name
or isCompleted
status.deleteTask
: Deletes a task by _id
, returning a boolean status.This schema enables task management with querying, filtering, sorting, and CRUD operations via GraphQL.
You may have noticed that we don’t filter by orgId
. This is because we will automatically extract it from the authorisation tokens. You can see how in the ntext section.
Now that we have a task service and a GraphQL schema, we need to connect them via resolvers.
taskService
as a plugin, extending the fastify
instance with it.contextFunction
with fastify.taskService
, so they are available in the resolvers.Query
getTasks
– Fetches the tasks using taskService.getTasks
, supporting filtering, sorting, and pagination. Requires authentication and extracts orgId
from the token to filter by.getTaskById
– Retrieves a single task using taskService.getTaskById
. Requires authentication.Mutation
createTask
– Creates a new task using taskService.createTask
, associating it with the authenticated user’s orgId
. Requires authentication and extracts orgId
from the token.updateTask
– Updates an existing task using taskService.updateTask
, allowing changes to name
and isCompleted
. Requires authentication and extracts orgId
from the token to ensure the task belongs to the user’s organisation.deleteTask
– Deletes a task using taskService.deleteTask
. Requires authentication and extracts orgId
from the token to ensure only tasks within the user’s organisation can be deleted.With the resolvers ready, we will extend the executableSchema
, merging the taskTypeDefs
together with their corresponding taskResolvers
.
Now that our API supports user and task management, the next step is to integrate it with our frontend app:
Here's how our timeline look like now: