← All @molecule/* packages · App templates
@molecule/api-resource-taskAPI resource · resource-task · API (Node) · v1.0.1 · Apache-2.0
Owner-scoped task CRUD with subtasks, priorities, due dates, position ordering
npm install @molecule/api-resource-task@molecule/api-resource-task is an API resource: the routes, validation and storage for resource-task, built on the database and auth cores so it runs on whichever providers your app has bonded.
import { createTaskRouter } from '@molecule/api-resource-task'
router.use('/tasks', createTaskRouter())Works with: @molecule/api-bonds-default-express, @molecule/api-database, @molecule/api-i18n, @molecule/api-middleware-validation
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
@molecule/api-resource-task — owner-scoped task CRUD with subtasks,
priorities, due dates, position ordering, recurrence rules, and
completion semantics.
Extracted from the to-do-list flagship app. Provides a ready-to-mount
Express router (createTaskRouter()) plus pure data-access functions
for use outside HTTP contexts.
import { createTaskRouter } from '@molecule/api-resource-task'
router.use('/tasks', createTaskRouter())
import { listTasksForOwner, createTaskForOwner } from '@molecule/api-resource-task'
const tasks = await listTasksForOwner(userId, { filter: 'today' })
const created = await createTaskForOwner(userId, { title: 'Ship release', priority: 1 })
resource
npm install @molecule/api-resource-task @molecule/api-bonds-default-express @molecule/api-database @molecule/api-i18n @molecule/api-middleware-validation express zod
npm install -D @types/express
TaskWire format — serialised for transport to the frontend.
interface Task {
id: string
title: string
description: string | null
priority: TaskPriority
due_date: string | null
due_time: string | null
parent_id: string | null
recurrence_rule: string | null
recurring: 'daily' | 'weekly' | 'monthly' | 'yearly' | null
completed: boolean
completed_at: string | null
position: number
created_at: string
updated_at: string
}
TaskRowDatabase row shape for a task.
interface TaskRow {
id: string
owner_id: string
parent_id: string | null
title: string
description: string | null
priority: TaskPriority
due_date: string | Date | null
due_time: string | null
recurrence_rule: string | null
position: number | string
is_completed: boolean
completed_at: string | Date | null
created_at: string | Date
updated_at: string | Date
}
TaskPriorityPriority levels — 1 (lowest) through 4 (highest).
type TaskPriority = 1 | 2 | 3 | 4
createTaskForOwner(ownerId, data)Create a new task owned by the given user and return the serialised Task.
function createTaskForOwner(
ownerId: string,
data: {
title: string
description?: string
parent_id?: string | null
priority?: number
due_date?: string | null
due_time?: string | null
recurrence_rule?: string | null
position?: number
},
): Promise<Task>
createTaskRouter()Creates and returns an Express Router with all task CRUD and reorder endpoints mounted.
function createTaskRouter(): Router
deleteTaskForOwner(taskId, ownerId)Delete a task owned by the given user; returns true if deleted, false if not found/owned.
function deleteTaskForOwner(taskId: string, ownerId: string): Promise<boolean>
getTaskForOwner(taskId, ownerId)Load a single task scoped to its owner. Returns null if missing or not owned.
function getTaskForOwner(taskId: string, ownerId: string): Promise<Task | null>
listTasksForOwner(ownerId, opts?)List tasks for an owner with optional filters.
function listTasksForOwner(
ownerId: string,
opts?: {
parent_id?: string | null
completed?: boolean
due_date?: string
filter?: 'today' | 'upcoming'
limit?: number
offset?: number
},
): Promise<Task[]>
reorderTasksForOwner(ownerId, items)Update the position field for a batch of tasks owned by the given user; returns the count of rows updated.
function reorderTasksForOwner(
ownerId: string,
items: { id: string; position: number }[],
): Promise<number>
toTask(row)Serialise a DB row into a wire-format Task.
function toTask(row: TaskRow): Task
updateTaskForOwner(taskId, ownerId, patch)Apply a partial patch to a task owned by the given user; returns the updated Task or null if not found/owned.
function updateTaskForOwner(
taskId: string,
ownerId: string,
patch: Partial<{
title: string
description: string | null
parent_id: string | null
priority: number
due_date: string | null
due_time: string | null
recurrence_rule: string | null
position: number
is_completed: boolean
}>,
): Promise<Task | null>
reorderSchemaValidates the request body for bulk-reordering tasks (array of id + position pairs).
const reorderSchema: z.ZodObject<
{ tasks: z.ZodArray<z.ZodObject<{ id: z.ZodString; position: z.ZodNumber }, z.core.$strip>> },
z.core.$strip
>
taskCreateSchemaValidates the request body for creating a new task.
const taskCreateSchema: z.ZodObject<
{
title: z.ZodString
description: z.ZodOptional<z.ZodString>
parent_id: z.ZodOptional<z.ZodNullable<z.ZodString>>
priority: z.ZodOptional<z.ZodNumber>
due_date: z.ZodOptional<z.ZodNullable<z.ZodString>>
due_time: z.ZodOptional<z.ZodNullable<z.ZodString>>
recurrence_rule: z.ZodOptional<z.ZodNullable<z.ZodString>>
position: z.ZodOptional<z.ZodNumber>
},
z.core.$strip
>
taskListQuerySchemaValidates query parameters for listing tasks (filtering, pagination, and due-date constraints).
const taskListQuerySchema: z.ZodObject<
{
parent_id: z.ZodOptional<z.ZodNullable<z.ZodString>>
completed: z.ZodOptional<z.ZodCoercedBoolean<unknown>>
due_date: z.ZodOptional<z.ZodString>
filter: z.ZodOptional<z.ZodEnum<{ today: 'today'; upcoming: 'upcoming' }>>
limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>
offset: z.ZodOptional<z.ZodCoercedNumber<unknown>>
},
z.core.$strip
>
taskRouterSingleton task router instance created from {@link createTaskRouter}.
const taskRouter: Router
taskUpdateSchemaValidates the request body for updating an existing task (all fields optional, plus completion flag).
const taskUpdateSchema: z.ZodObject<
{
title: z.ZodOptional<z.ZodString>
description: z.ZodOptional<z.ZodOptional<z.ZodString>>
parent_id: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>
priority: z.ZodOptional<z.ZodOptional<z.ZodNumber>>
due_date: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>
due_time: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>
recurrence_rule: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>
position: z.ZodOptional<z.ZodOptional<z.ZodNumber>>
is_completed: z.ZodOptional<z.ZodBoolean>
},
z.core.$strip
>
Peer dependencies:
@molecule/api-bonds-default-express ^1.0.1@molecule/api-database ^1.0.1@molecule/api-i18n ^1.0.1@molecule/api-middleware-validation ^1.0.1express ^5.0.0zod ^4.0.0@molecule/api-bonds-default-express@molecule/api-database@molecule/api-i18n@molecule/api-middleware-validationexpresszodSession-auth prerequisite: every route reads the caller via
requireUser(res) (res.locals.session.userId, 401 fail-closed) — mount
createTaskRouter() behind your global auth middleware. All queries are
owner-scoped through the *ForOwner service functions using that session
id; never pass a client-supplied owner id. Unlike declarative-route
resources there is no routes/requestHandlerMap export — this package
ships the Express router factory shown above (plus a taskRouter
singleton).
Tables: src/__setup__/tasks.sql creates tasks. An mlcl-scaffolded API
replays __setup__/*.sql automatically on migrate; anywhere else run it
once — nothing at runtime creates them.
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
completed and stamps completed_at: the
task leaves the active list and shows in the completed view; toggling it
back clears completed_at and returns it to the active list.today shows only incomplete
tasks due today, upcoming only incomplete tasks that have a due date, the
completed view only completed tasks, and opening a task's subtasks lists
only its children (parent_id) — never the whole task list.*ForOwner): a user sees and mutates only their OWN tasks. Guessing or
tampering another user's task id on GET/PUT/DELETE /:id returns 404 and
never that task's data; slipping a foreign id into a reorder batch leaves
that task's position unchanged (it is skipped, not moved).