Skip to content

Work with tasks in generated screens

View as Markdown

The project and task schemas from the previous page already describe an ordinary record workflow. This page traces that metadata into generated list, create, detail, and edit screens, then follows the matching requests in the browser. You will create related records, edit and find a task, and navigate from a project to its tasks without adding a project-owned React route. The same pattern supplies the first usable workflow for other table-shaped application data.

Use the generated project and task screens with me. I want to create related records, edit a task, search it, and confirm the browser uses the generated table API.

The generated experience follows the types, Drizzle foreign key, and Sapporta metadata defined on the previous page:

  • rowLabelColumns: ["name"] displays project names in task lookups.
  • select() options render status and priority as searchable, clearable comboboxes.
  • date() renders a date editor and preserves the canonical YYYY-MM-DD wire value.
  • search.columns activates search across task title and description.
  • the project’s children entry adds a Tasks collection to project detail.
  • row-scope columns and managed timestamps stay out of ordinary forms and grids.

Presentation metadata does not authorize data. Generated lookups, lists, gets, and writes still run through the current request’s abilities and row-visibility policy on the server.

Start the app and inspect the generated create operations before using the screens:

Terminal window
pnpm dev
pnpm exec sapporta endpoints show "POST /api/tables/projects"
pnpm exec sapporta endpoints show "POST /api/tables/tasks"

Open /tables/projects in an authenticated browser and create a project named Website Relaunch. Then open /tables/tasks/new and enter:

FieldValue
TitlePublish launch checklist
ProjectWebsite Relaunch
Statusin_progress
Prioritymedium
Due date2026-08-01
DescriptionPrepare the release list

The Project field stores the numeric project ID and displays the project name. Its picker reads the generated lookup route:

GET /api/tables/projects/_lookup?q=Website
{
"entries": [
{
"value": 1,
"label": "Website Relaunch",
"meta": { "id": 1, "name": "Website Relaunch" }
}
]
}

Lookup values preserve the referenced primary-key type. This project uses an integer key, so the task request sends project_id as a JSON number.

Status and priority use local option lists from their select() column declarations. Text entered in either combobox filters those options and does not become the saved value. The chosen option remains the exact declared string.

Open the browser Network panel before saving. The generated form sends this request under the framework /api prefix:

POST /api/tables/tasks
Content-Type: application/json
{
"project_id": 1,
"title": "Publish launch checklist",
"description": "Prepare the release list",
"status": "in_progress",
"priority": "medium",
"due_date": "2026-08-01"
}

The form decodes its draft on submit. It preserves invalid field text when local decoding fails and sends finite numbers or canonical date values only after a successful decode. The server then adds trusted scope values, runs visible-reference checks, performs authoritative structural and application validation, writes the parsed row, and returns HTTP 201. The relevant response fields are:

{
"data": {
"id": 1,
"project_id": 1,
"title": "Publish launch checklist",
"description": "Prepare the release list",
"status": "in_progress",
"priority": "medium",
"due_date": "2026-08-01"
}
}

The request body omits IDs, timestamps, and scope columns. Clients do not choose workspace, owner, role, or scope values. The API derives them from request authority.

Open the saved task and change its status to open. The browser sends a generated update request with a patch-shaped body even though the HTTP method is PUT:

PUT /api/tables/tasks/1
Content-Type: application/json
{ "status": "open" }
{
"data": {
"id": 1,
"project_id": 1,
"title": "Publish launch checklist",
"status": "open",
"priority": "medium",
"due_date": "2026-08-01"
}
}

Generated row updates use PUT, not PATCH. The server applies the update to the requested ID and the active row-visibility predicate in the same write.

Return to /tables/tasks and search for launch. Filter status to open with the searchable multi-value combobox and confirm the saved task remains. The chosen value appears as a removable chip; the combobox query itself is not part of the filter. Then open Website Relaunch and find the task in its Tasks child collection. The lookup, task list, task detail, and child list all use generated routes with the same row boundary.

The task app now has a complete generated record loop: create, validate, edit, search, lookup, detail, and parent-child navigation. The browser requests show that these screens are clients of the same generated CRUD API, and hidden fields remain presentation rather than authorization. Continue to Query tasks through the generated API to call those routes directly. The record screens and forms guide and generated record surfaces reference describe the available generated behavior in more depth.