Skip to main content

Listing tasks

We want to admire our tasks, so let's list them!

Introducing operations (queries and actions)#

The primary way of interacting with entities in Wasp is via operations (queries and actions).

Queries are here when we need to fetch/read something, while actions are here when we need to change/update something. We will start with writing a query, since we are just listing tasks and not modifying anything for now.

To list tasks, we will need two things:

  1. Wasp query that fetches all the tasks from the database.
  2. React logic that calls our query and displays its results.

Wasp query#

Let's implement getTasks query. It consists of a declaration in Wasp and implementation in JS (in ext/ directory).

Wasp declaration#

Add the following code to main.wasp:

// ...
query getTasks {  // We specify that JS implementation of the query (which is an async JS function)  // can be found in `ext/queries.js` as named export `getTasks`.  fn: import { getTasks } from "@ext/queries.js",  // We tell Wasp that this query is doing something with entity `Task`. With that, Wasp will  // automatically refresh the results of this query when tasks change.  entities: [Task]}

JS implementation#

Next, create a new file ext/queries.js and define the JS function that we just imported in the query declaration above:

export const getTasks = async (args, context) => {  return context.entities.Task.findMany({})}

Query function parameters:

  • args: object, arguments the query is invoked with.
  • context: object, additional stuff provided by Wasp.

Since we declared in main.wasp that our query uses entity Task, Wasp injected a Prisma client for entity Task as context.entities.Task - we used it above to fetch all the tasks from the database.


Queries and actions are NodeJS functions that are executed on the server.

Invoking the query from React#

Finally, let's use the query we just created, getTasks, in our React component to list the tasks:

import React from 'react'
import getTasks from '@wasp/queries/getTasks'import { useQuery } from '@wasp/queries'
const MainPage = () => {  const { data: tasks, isFetching, error } = useQuery(getTasks)
  return (    <div>      {tasks && <TasksList tasks={tasks} />}
      {isFetching && 'Fetching...'}      {error && 'Error: ' + error}    </div>  )}
const Task = (props) => (  <div>    <input      type='checkbox' id={}      checked={props.task.isDone} readonly    />    {props.task.description}  </div>)
const TasksList = (props) => {  if (!props.tasks?.length) return 'No tasks'  return, idx) => <Task task={task} key={idx} />)}
export default MainPage

All of this is just regular React, except for the two special @wasp imports:

  • import getTasks from '@wasp/queries/getTasks': provides us with our freshly defined Wasp query.
  • import { useQuery } from '@wasp/queries': provides us with Wasp's useQuery React hook which is actually just a thin wrapper over react-query's useQuery hook, behaving very similarly while offering some extra integration with Wasp.

While we could call query directly as getTasks(), calling it as useQuery(getTasks) gives us reactivity- the React component gets re-rendered if the result of the query changes.

With these changes, you should be seeing the text "No tasks" on the screen:

Todo App - No Tasks

Next, let's create some tasks!