Skip to main content
Have a Wasp app in production? 🐝 We'll send you some swag! πŸ‘•
This is documentation for Wasp 0.13.0, which is no longer actively maintained.
For up-to-date documentation, see the latest version (0.20.0).
Version: 0.13.0

6. Modifying Data

In the previous section, we learned about using Queries to fetch data and only briefly mentioned that Actions can be used to update the database. Let's learn more about Actions so we can add and update tasks in the database.

We have to create:

  1. A Wasp Action that creates a new task.
  2. A React form that calls that Action when the user creates a task.

Creating a New Action​

Creating an Action is very similar to creating a Query.

Declaring an Action​

We must first declare the Action in main.wasp:

  • JavaScript
  • TypeScript
main.wasp
// ...

actioncreateTask{
fn: import{ createTask }from"@src/actions",
entities: [Task]
}

Implementing an Action​

Let's now define a function for our createTask Action:

  • JavaScript
  • TypeScript
src/actions.js
exportconstcreateTask=async(args, context)=>{
return context.entities.Task.create({
data:{description: args.description},
})
}
tip

We put the function in a new file src/actions.ts, but we could have put it anywhere we wanted! There are no limitations here, as long as the declaration in the Wasp file imports it correctly and the file is located within src directory.

Invoking the Action on the Client​

Start by defining a form for creating new tasks.

  • JavaScript
  • TypeScript
src/MainPage.jsx
import{
createTask,
getTasks,
useQuery
}from'wasp/client/operations'

// ... MainPage, TaskView, TaskList ...

constNewTaskForm=()=>{
consthandleSubmit=async(event)=>{
event.preventDefault()
try{
const target = event.target
const description = target.description.value
target.reset()
awaitcreateTask({ description })
}catch(err){
window.alert('Error: '+ err.message)
}
}

return(
<formonSubmit={handleSubmit}>
<inputname="description"type="text"defaultValue=""/>
<inputtype="submit"value="Create task"/>
</form>
)
}

Unlike Queries, you can call Actions directly (i.e., without wrapping it with a hook) because we don't need reactivity. The rest is just regular React code.

All that's left now is adding this form to the page component:

  • JavaScript
  • TypeScript
src/MainPage.jsx
import{
createTask,
getTasks,
useQuery
}from'wasp/client/operations'

constMainPage=()=>{
const{data: tasks, isLoading, error }=useQuery(getTasks)

return(
<div>
<NewTaskForm/>

{tasks &&<TasksListtasks={tasks}/>}

{isLoading &&'Loading...'}
{error &&'Error: '+ error}
</div>
)
}

// ... TaskView, TaskList, NewTaskForm ...

Great work!

You now have a form for creating new tasks.

Try creating a "Build a Todo App in Wasp" task and see it appear in the list below. The task is created on the server and saved in the database.

Try refreshing the page or opening it in another browser. You'll see the tasks are still there!

Todo App - creating new task

Automatic Query Invalidation

When you create a new task, the list of tasks is automatically updated to display the new task, even though we have not written any code that would do that! Wasp handles these automatic updates under the hood.

When you declared the getTasks and createTask operations, you specified that they both use the Task entity. So when createTask is called, Wasp knows that the data getTasks fetches may have changed and automatically updates it in the background. This means that out of the box, Wasp keeps all your queries in sync with any changes made through Actions.

This behavior is convenient as a default but can cause poor performance in large apps. While there is no mechanism for overriding this behavior yet, it is something that we plan to include in Wasp in the future. This feature is tracked here.

A Second Action​

Our Todo app isn't finished if you can't mark a task as done.

We'll create a new Action to update a task's status and call it from React whenever a task's checkbox is toggled.

Since we've already created one task together, try to create this one yourself. It should be an Action named updateTask that receives the task's id and its isDone status. You can see our implementation below.

Solution

Declaring the Action in main.wasp:

  • JavaScript
  • TypeScript
main.wasp
// ...

actionupdateTask{
fn: import{ updateTask }from"@src/actions",
entities: [Task]
}

Implementing the Action on the server:

  • JavaScript
  • TypeScript
src/actions.js
// ...

exportconstupdateTask=async({ id, isDone }, context)=>{
return context.entities.Task.update({
where:{ id },
data:{
isDone: isDone,
},
})
}

You can now call updateTask from the React component:

  • JavaScript
  • TypeScript
src/MainPage.jsx
// ...
import{
updateTask,
createTask,
getTasks,
useQuery,
}from'wasp/client/operations'

// ... MainPage ...

constTaskView=({ task })=>{
consthandleIsDoneChange=async(event)=>{
try{
awaitupdateTask({
id: task.id,
isDone: event.target.checked,
})
}catch(error){
window.alert('Error while updating task: '+ error.message)
}
}

return(
<div>
<input
type="checkbox"
id={String(task.id)}
checked={task.isDone}
onChange={handleIsDoneChange}
/>
{task.description}
</div>
)
}
// ... TaskList, NewTaskForm ...

Awesome! You can now mark this task as done.

It's time to make one final addition to your app: supporting multiple users.

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /