Middleware
Middleware layer that transforms Ellucian Ethos API entities into Canvas Data Portal API calls for real-time enrollment, grade, and course synchronization.
This middleware integration connects Ellucian Ethos as the authoritative data source to Canvas LMS via the Canvas Data Portal and REST APIs, enabling institutions to keep their learning management system synchronized with student information held in any Ethos-compliant SIS. The integration listens for Ethos change events (person, enrollment, term, course section), transforms those canonical entities into Canvas API payloads, and orchestrates upserts with idempotency guarantees.
The complexity lies in bridging two fundamentally different data models: Ethos exposes normalized, resource-oriented entities (e.g., persons, enrollments) while Canvas uses its own course-centric ontology and exposes both a traditional REST API and a separate Data Portal API with different schemas and rate limits. Additionally, Canvas enforces aggressive throttling (typically 1,000 requests/minute per instance), requiring the middleware to implement adaptive rate limiting, batch chunking for large enrollment imports, and conflict detection when the same record is updated simultaneously in both systems.
A working piece from this integration — no sign-up. The full build handles the edge cases, safeguards, and cutover.
// Ellucian Ethos → Canvas LMS Middleware Transform const ethosPerson = { id: "8a2e9c51-7b3d-4f1a-8c6e-2d5b4a3f9e7c", names: { givenName: "Maria", familyName: "Gonzalez" }, emails: [{ address: "[email protected]", type: "work" }], demographics: { birthDate: "1998-03-15" } }; const canvasUserPayload = { user: { name: `${ethosPerson.names.givenName} ${ethosPerson.names.familyName}`, first_name: ethosPerson.names.givenName, last_name: ethosPerson.names.familyName, email: ethosPerson.emails.find(e => e.type === "work")?.address, integration_id: ethosPerson.id, skip_reconfirmation: true } }; // POST /api/v1/accounts/:account_id/users await canvasLMS.createUser(canvasUserPayload); // Key Entity Mapping Table: // ┌─────────────────────┬──────────────────────────────┐ // │ Ethos Entity │ Canvas Data Portal Field │ // ├─────────────────────┼──────────────────────────────┤ // │ person.id │ user.integration_id │ // │ person.names.* │ user.name, first_name, last_ │ // │ person.emails[] │ user.email │ // │ student.sectionIds │ enrollment.course_section_id │ // │ student.standing │ enrollment.enrollment_state │ └─────────────────────┴──────────────────────────────┘
How we'd take this from discovery to a production-safe cutover — the phases, the canonical mapping, and the edge cases that bite.
This integration layer sits between Ellucian Ethos and Canvas Data Portal, extracting institutional data from the SIS via Ethos REST APIs and pushing transformed records into Canvas via the Data Portal API. The middleware handles entity resolution, attribute mapping, and batch synchronization for enrollment status, grade submissions, and course rosters on a configurable schedule.
READ_PERSONS, READ_ENROLLMENTS, and READ_COURSES scopes. Store credentials in a secrets manager; do not hardcode.GET /meta/{entity-name} to capture attribute names, data types, and cardinality. Pull Canvas Data Portal data dictionary from the /api/v1/accounts/{account_id}/reports endpoint to confirm target column names. Document drift between source and target schemas.persons to Canvas users, enrollments to Canvas enrollments, and sections to Canvas courses. Build a mapping configuration file (JSON or YAML) that pairs each Ethos field identifier to its Canvas Data Portal equivalent, including transformation rules (e.g., date format, status code translation).GET /persons and GET /enrollments using cursor-based pagination. On transform, apply field mapping, null coalescing, and business-rule logic. On load, call Canvas POST /api/v1/courses/{course_id}/enrollments or grade submission endpoints.SHA256(personId + sectionId + termId). Store processed keys in a ledger table (Postgres or Redis). On each run, check the ledger before posting to Canvas; skip records already synchronized for the current academic term.GET /enrollments?updatedAfter={lastSyncTimestamp} for incremental pulls. Track the lastModifiedDateTime from each Ethos record. For grades, use the academicRecords endpoint filtered by gradedAfter. Store lastSyncTimestamp in the middleware database to persist across restarts.| Ethos Entity | Ethos Field | Canvas Target | Canvas Field | Transform Rule |
|---|---|---|---|---|
persons |
guid |
users |
integration_id |
Direct pass-through; used as Canvas unique identifier |
persons |
email |
users |
login |
Direct pass-through; normalized to lowercase |
enrollments |
status |
enrollments |
enrollment_state |
Map active → active; inactive → inactive; waitlist → invited |
enrollments |
courseSectionId |
courses |
sis_source_id |
Direct pass-through; resolve via sections entity lookup |
academicRecords |
gradedOn |
submissions |
submitted_at |
Convert ISO 8601 to Unix epoch seconds |
academicRecords |
grade |
submissions |
posted_grade |
Map letter grade to Canvas point scale via institution grading scheme; default to 0 on null grade |
sections |
termId |
courses |
enrollment_term_id |
Direct pass-through; matches Canvas term identifier |
guid changes (duplicate merge), the middleware must detect the old integration_id in Canvas and re-associate, or the new record creates a duplicate user.termId is not included in the idempotency key.updatedAfter timestamp falls within a page boundary during high-volume periods, records on that boundary may be skipped in one run and duplicated in the next. Use cursor pagination when available instead of offset.email in Ethos. Canvas requires a unique login; the middleware must generate a deterministic placeholder (e.g., [email protected]) and flag for ITS review.Cutover proceeds in three steps: (1) enable the middleware in read-only validation mode for one full sync cycle and confirm zero record-level discrepancies between Ethos source counts and Canvas target counts; (2) switch to read-write mode, maintaining the rollback ledger — if discrepancies exceed a 0.1% threshold, execute DELETE calls from the audit log to restore Canvas to pre-sync state; (3) after 48 hours of stable read-write operation, archive the rollback scripts and promote the integration to production monitoring with weekly reconciliation reports. All cutover actions must be logged with timestamps, operator ID, and affected record counts to support post-incident review.
Reading the reference is free. Delivering it under liability — with the safeguards that keep production running through the cutover — is what we do.