Events API (calendar & scheduling)
This document describes the event and calendar endpoints implemented in ILS-dev/backend/routes/events.js. It is a factual extraction of the current implementation and makes no changes to ILS‑dev.
Overview
- Primary route:
/api/events - Purpose: provide a unified calendar feed used by teacher, student and admin views. The endpoint returns computed events (assignment due/open, quiz/exam due), custom calendar events (with recurrence expansion), and academic calendar events expanded into per-day entries.
Authentication & permissions
GET /api/events— public for authenticated/unauthenticated callers (the code requiresfrom/tobut does not require req.user to be set; student/teacher/class filters are supported).POST /api/events,PATCH /api/events/:id,DELETE /api/events/:id— require an authenticated user (req.user?.idis checked). The implementation does not perform explicit owner or role checks in this file (it readscreated_byon update but does not enforce ownership here).
Key endpoints
- GET /api/events
- Query params (required/optional):
from(ISO date string, required)to(ISO date string, required)classId(optional) — filter events to a single classstudentId(optional) — include events for classes the student is enrolled inteacherId(optional) — include events for classes the teacher teaches or events the teacher created
- Returns: unified array of event objects with fields including
id,type,title,date,starts_at,ends_at,all_day,entity_type,entity_id,class_id,class_name,description,location, and recurrence metadata when applicable. -
Computed event types:
assignment_due— fromassignments.due_dateassignment_open— fromassignments.available_fromquiz_due/exam— fromquizzes.due_date(quiz_typeexammaps toexam)calendar_event— custom events fromcalendar_events(may be recurring; expanded byexpandRecurring())academic_*— institution-level academic calendar events expanded into per-day entries
-
POST /api/events
- Body:
{ event_type, title, description?, location?, frequency?, frequency_until?, starts_at, ends_at?, all_day?, class_id?, course_id? } -
Requires
titleandstarts_at— returns201with the created row. -
PATCH /api/events/:id
- Body may include any of the updatable fields:
title,description,location,frequency,frequency_until,starts_at,ends_at,all_day,event_type,class_id. - Returns the updated event row.
-
If no fields provided, returns
400withNo fields to update. -
DELETE /api/events/:id
- Soft-deletes the event by setting
deleted_at = now()and returns204on success.
Recurrence & expansion behavior
expandRecurring(event, from, to)supports frequencies:daily,weekdays,weekly,monthly,yearly, anddoes_not_repeat.- The implementation normalizes dates using noon (
T12:00:00) to avoid DST edge cases and limits expansions to 500 iterations to prevent runaway loops. - Recurring event IDs in the returned feed are synthesized as
${event.id}-${YYYY-MM-DD}so each occurrence has a unique id. - Academic calendar multi-day events are expanded into one entry per date in the query window.
Filtering logic notes
- If
classIdis provided, the feed is scoped to that class's modules and custom events. - If
studentIdis provided the server resolves active enrollments (class_enrollments) for that student and includes events for those classes. - If
teacherIdis provided the server resolves classes the teacher teaches (class_teachersandclass_module_teachers) and includes events for those classes; additionally it includes events the teacher created with no class.
Sample requests
GET events for a week (JSON):
curl "http://localhost:4000/api/events?from=2026-09-01&to=2026-09-07" \
-H "Accept: application/json"
Create a custom recurring event (authenticated):
curl -X POST "http://localhost:4000/api/events" \
-H "Authorization: Bearer <JWT>" \
-H "Content-Type: application/json" \
-d '{"event_type":"custom","title":"Weekly Lab","starts_at":"2026-09-01T09:00:00","frequency":"weekly","class_id":123}'
Implementation notes & caveats
- The feed merges computed events (assignments/quizzes) with stored
calendar_eventsandacademic_calendar_events. Check the SQL in the source if you need exact column shapes or filtering nuances. - The code constructs several
idprefixes (assignment-due-,assignment-open-,quiz-due-,custom-,academic-) to indicate origin — frontends may rely on these ids for deduplication or grouping. - The route performs direct SQL via
sequelize.query(...)and depends onmodule_items/class_modulesjoins to link assignments/quizzes to classes.
Source (read-only): ILS-dev/backend/routes/events.js
Last updated: 2026-07-08