Creating a Task
Creating a Task
~13 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
Chapter 41 showed TWO ways to create a task: the FLAT /api/tasks resource WITH an explicit project IRI field, or the nested endpoint. The frontend uses the FIRST way HERE, since the IRI construction stays EXPLICIT and TRACEABLE.
The create task hook
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { apiClient } from '../api/client';
import type { Task } from '../types/task';
interface CreateTaskInput {
title: string;
projectId: number;
}
export function useCreateTask() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ title, projectId }: CreateTaskInput) => {
const response = await apiClient.post<Task>('/tasks', {
title,
project: `/projects/${projectId}`,
});
return response.data;
},
onSuccess: (_data, variables) => {
queryClient.invalidateQueries({
queryKey: ['projects', variables.projectId, 'tasks'],
});
},
});
}project: \`/projects/${projectId}\` MANUALLY builds the IRI from the numeric id – EXACTLY the format chapter 37 ESTABLISHED as REQUIRED for relationship fields.
Achtung: onSuccess(_data, variables) uses the SECOND parameter to access projectId – mutationFn's RETURN value (_data, DELIBERATELY unused here and thus marked with an underscore) contains ONLY the new task, NOT the ORIGINAL input data.
The form on the detail page
function CreateTaskForm({ projectId }: { projectId: number }) {
const [title, setTitle] = useState('');
const createTask = useCreateTask();
function handleSubmit(event: FormEvent) {
event.preventDefault();
createTask.mutate(
{ title, projectId },
{ onSuccess: () => setTitle('') },
);
}
return (
<form onSubmit={handleSubmit}>
<input value={title} onChange={(e) => setTitle(e.target.value)} />
<button type="submit">Add task</button>
</form>
);
}EXACTLY the same two-callback pattern as chapter 78 (onSuccess in the hook FOR the cache, onSuccess on the call FOR the form itself) – A RECURRING, CONSISTENT pattern across ALL create forms in this course.
Tipp: EXACTLY as chapter 41 showed that an INVALID project IRI returns a 422 with propertyPath: "project", the fieldError mechanism from chapter 79 APPLIES HERE TOO, WITHOUT having to write CUSTOM error handling.