Module Composition & Locking API
This page documents the Module composition endpoints that manage ordered module items (assignments, quizzes, projects) and per-item lock rules. All information is a read-only extraction from ILS-dev/backend/routes/moduleItems.js.
Base path
- Router mounted at:
GET|POST|PATCH|DELETE /api/modules/:id/items(router usesmergeParamsthus:idis the module id).
Endpoints
- GET /api/modules/:id/items
- Purpose: return ordered items for the module (assignments + quizzes).
- Response: JSON array of rows ordered by
positionwith these fields (selected columns): id,module_id,content_type(assignment|quiz|project),content_id,positiontitle,status,due_date(text),points_possible,rubric_total_points,quiz_type,item_countlocked_by_previous,unlock_on
Notes:
- The SQL used by the endpoint joins only assignments and quizzes, and filters out module items whose referenced assignment/quiz row is missing; as a result, project content items (accepted by POST) are not returned by this query.
Example response (abridged):
[
{
"id": 101,
"module_id": 12,
"content_type": "assignment",
"content_id": 55,
"position": 10,
"title": "Research Essay",
"due_date": "2026-06-01T23:59:00",
"rubric_total_points": "20",
"locked_by_previous": true,
"unlock_on": "grade"
}
]
- POST /api/modules/:id/items
- Purpose: add a new content item to a module.
- Body:
{ content_type: 'assignment'|'quiz'|'project', content_id: number }(JSON) - Behavior:
- Computes
positionasCOALESCE(MAX(position), -10) + 10so new items are appended with spacing. ON CONFLICT (module_id, content_type, content_id) DO NOTHING— duplicates are detected server-side.- Returns
201with created item, or409if item already exists.
Example request:
{ "content_type": "assignment", "content_id": 55 }
- DELETE /api/modules/:id/items/:itemId
- Purpose: remove an item from a module.
-
Returns
204on success,404if not found. -
PATCH /api/modules/:id/items/reorder
- Purpose: bulk update positions for multiple module items.
- Body:
{ items: [{ id: number, position: number }, ...] }(JSON) - Behavior: Updates each
module_items.positioninside a DB transaction; returns{ ok: true }on success. -
Note: this route is declared before
/:itemIdin the router to avoid the literalreorderpath being interpreted as anitemId. -
PATCH /api/modules/:id/items/:itemId
- Purpose: update lock settings for a single module item.
- Body:
{ locked_by_previous?: boolean, unlock_on?: 'submit'|'grade'|'pass' }(JSON) - Validation:
unlock_onmust be one ofsubmit,grade, orpassif provided. - Behavior: updates provided fields and
updated_at, returns the updated item. Returns400for invalid inputs or when there are no fields to update,404if item not found.
Implementation notes & gotchas
- Position spacing: positions are incremented by 10 on insert to allow later inserts between items without a full reorder.
projectcontent type is accepted by the POST endpoint but the GET listing SQL only returnsassignmentandquizrows — projects won't appear in the GET response until the SQL is extended.- Reordering is performed inside a transaction, so partial failures roll back.
- Locking semantics:
locked_by_previous(boolean) — when true, the item is considered locked until the previous item meets theunlock_oncondition.unlock_on(enum) — one ofsubmit,grade, orpassdescribing which event on the previous item unlocks this one.
Where this is used
- Teacher Module Composer UI (frontend) calls these endpoints to display, add, remove, reorder, and set locks for module items.
References (read-only)
ILS-dev/backend/routes/moduleItems.js
Last updated: 2026-07-08