@faiber/faiber-lms
v0.19.0
Published
Courses, classrooms, homework/project banks and assignments, exam banks/items/sessions/users, certificates, reports, and education configuration.
Readme
@faiber/faiber-lms
Courses, classrooms, homework/project banks and assignments, exam banks/items/sessions/users, certificates, reports, and education configuration.
Install
npm install @faiber/faiber-lmsConfigure
import { FaiberClient, MemoryTokenProvider } from "@faiber/sdk-core";
import { LmsApi } from "@faiber/faiber-lms";
const tokens = new MemoryTokenProvider();
const client = new FaiberClient("lms", {
domains: { lms: process.env.FAIBER_LMS_URL! },
tokenProvider: tokens,
axios: { timeout: 15_000, withCredentials: true },
});
const api = new LmsApi(client);
const courses = await api.courses.list({
"page[number]": 1,
"page[size]": 20,
});
const sessions = await api.courseSessions(courseId);Complete capability
This package exposes 164 registered operations from the learning management service. Common workflows have concise methods on api; every registered backend route is also available as a named function on api.operations. Generated operation input, query, response, path, verb, and permission contracts are exported from operations.types.
| Area | Operations | HTTP methods |
|---|---:|---|
| academy | 8 | GET, POST |
| ai-summary | 8 | GET, POST, PUT |
| branding | 5 | GET, POST, PUT |
| certificate | 10 | GET, PATCH, POST |
| classroom | 20 | DELETE, GET, PATCH, POST, PUT |
| config | 20 | GET, PATCH, POST |
| course | 17 | DELETE, GET, PATCH, POST |
| dashboard | 1 | GET |
| docs | 1 | GET |
| drm-routes | 1 | GET |
| evaluation | 4 | GET, POST, PUT |
| exam | 22 | DELETE, GET, PATCH, POST, PUT |
| homework | 24 | DELETE, GET, PATCH, POST |
| integration | 7 | GET, POST |
| media | 2 | GET, POST |
| profile-routes | 2 | GET |
| report | 8 | GET |
| router | 2 | GET |
| service | 1 | GET |
| session | 1 | GET |
Course sessions, classroom users and absences, assignments, invitations, club projects, support interactions, and work-time records have dedicated typed operations. Most LMS updates use PATCH; unsupported generic deletes are guarded locally.
Homework assignments and exam sessions expose explicit student_user_id, teacher_user_id, consultant_user_id, and support_user_id list filters. The existing user_id filter remains available when a caller wants any related participant or staff role.
Homework-bank items accept name on create and update. Responses always include name; when it is blank or omitted, the LMS derives it from the question text and caps it at 80 Unicode characters with an ellipsis.
Homework and exam banks
Reusable homework definitions are the homework-bank layer: api.homeworkBanks and the compatibility alias api.homeworks address the same records. Use api.homeworkBankItems(bankId) for the definition's questions/items (homework_questions). Every item has kind: "todo" | "project"; the nested list accepts the same kind filter, and create/update accept the field. Prefer api.homeworkAssignments.createForItem(item, delivery) to select the exercise ID explicitly. Delivery records reference an item's ID (not the bank ID) and are fully managed through api.homeworkAssignments. The compatibility methods listAssignments, createAssignment, assignment, updateAssignment, and deleteAssignment remain available.
Exam definitions are exposed as api.examBanks, their questions/items as api.examBankItems, delivery sessions as api.examSessions, and learner attempts as listExamUsers, examUser, and updateExamUser. The legacy api.exams, api.examQuestions, and api.homeworks names remain available.
const bank = await api.homeworkBanks.create({ name: "Projects", status: "active" });
const items = api.homeworkBankItems(bank.data.data.id);
const item = await items.create({ question_text: "Build a recursion demo", question_type: "answer", kind: "project", status: "active" });
// item.data.data.id is the exercise; item.data.data.homework_id is its parent bank.
const delivery = await api.homeworkAssignments.createForItem(item.data.data, {
user_id: studentUserId,
classroom_id: classroomId,
status: "pending",
});
const todos = await items.list({ kind: "todo" });
const assignments = await api.homeworkAssignments.list({
statuses: ["pending", "unsolved", "completed"],
homework_id: item.data.data.id,
});Session attendance rows expose attendance_mode (online or in_person) alongside the stored online boolean, attended seconds, description, and ratings. The same details are available on absence rows. When recording an attendance batch, include online for each learner whose attendance mode matters; omitting it defaults to in-person.
Classroom weekly schedules
Use day_of_week (Sunday 0 through Saturday 6) and starts_at (HH:MM or HH:MM:SS) for new classroom schedules. Set timezone_offset_minutes when the local time differs from the offset in the classroom's starts_at timestamp.
await api.classrooms.create({
course_id: courseId,
name: "Autumn class",
starts_at: "2026-09-20T09:08:11.383Z",
status: "active",
weekly_schedule: [
{ day_of_week: 0, starts_at: "15:00", timezone_offset_minutes: 210, mode: "online" },
{ day_of_week: 2, starts_at: "14:00", timezone_offset_minutes: 210, mode: "interactive" },
],
});The weekly mode selects each generated primary session type: online → ONLINE_CLASS, interactive → VIDEO (composition media), in_person → MEETING, and makeup → REWIEW_CLASS. You can instead supply a course_session_type_id. Updating weekly_schedule on a classroom also reschedules its existing generated sessions. For existing forms, the LMS accepts day (Persian or English weekday name) with start_time; it normalizes these fields on save. Legacy delivery_type and session_type are supported when mode is absent, with delivery_type taking precedence. A Persian day without an explicit offset defaults to Tehran time (+03:30).
When moving one classroom session, opt in to shifting every later session onto the next weekly schedule slots after the new start time:
await api.classroomSessions.update(sessionId, {
starts_at: "2026-09-23T10:00:00Z",
shift_following_sessions: true,
});If the selected session or an affected later session already has a provisioned room, the LMS returns 409 recording_reset_confirmation_required. Repeat the request with confirm_recording_reset: true only after the user accepts that the affected recordings will be removed and those rooms reset.
Authentication and authorization
Use a TokenProvider to forward the signed-in user's Bearer token, or enable withCredentials for secure HttpOnly cookie sessions. Permission requirements copied from service route guards are included in each operation's JSDoc. The SDK does not embed API keys, credentials, localhost URLs, sandbox hosts, or production hosts.
Inputs, queries, responses, and transport
All methods return the complete Axios response, including status, headers, and request IDs. Request bodies and query objects are named exported interfaces. Nested pagination uses keys such as page[number] and page[size] where required by the service. Standard Axios request options, headers, timeouts, adapters, interceptors, and AbortSignal cancellation are supported as the final argument.
Generic REST resources are capability-guarded. Calling a legacy method that the service does not register throws UnsupportedOperationError before sending a request instead of producing a backend 405.
Errors and cancellation
try {
await api.client.get("/health", undefined, { signal: AbortSignal.timeout(5_000) });
} catch (error) {
// Axios errors retain response.status and the service error body.
}Use @faiber/faiber-ts-sdk when one application needs multiple Faiber services with one configuration.
Versioned interactive learning
api.interactive exposes published previews, authored definition versions, start/resume, draft saves, progressive hints, deterministic submissions and bounded teacher context. The deployment must include the interactive-learning migration and executor. The previous unmounted interactiveContent resource now fails locally; use the supported versioned API.
const catalog = await api.interactive.catalog();
const started = await api.interactive.start(sessionId, { enrollment_id: enrollmentId });
const run = started.data.data;
const result = await api.interactive.submit(run.state.id, {
revision: run.state.revision,
activity_id: run.definition.activities[run.state.current_activity].id,
code: "score = 10",
idempotency_key: crypto.randomUUID(),
});The server owns checks and mastery. Browser output must not be submitted as a passing result. Keep the idempotency key for retries of an identical submission; refetch on revision conflict. Definition authoring requires lms:course:read/lms:course:update; learner mutations verify active enrollment ownership and prerequisites. No API credentials belong in browser storage.
Question, option, homework, and course images
Use the same authenticated upload endpoint for all LMS images. File (browser) and Blob
(browser or Node 18+) are supported; accepted formats are PNG, JPEG, WebP, and GIF up to 10 MiB.
const upload = await lms.media.uploadImage(file, {
fileName: 'homework.webp',
signal: abortController.signal,
onUploadProgress: ({ loaded, total }) => {
if (total) updateUploadPercent(Math.round(loaded * 100 / total));
},
});
const imageUrl = upload.data.data?.url;
if (!imageUrl) throw new Error("Upload did not return an image URL");The helper sends the multipart image field and inherits the SDK's IDP credentials and
refresh flow. Do not set a multipart boundary yourself. Native Axios progress reports bytes
sent; await the response before marking the upload successful, because storage can still fail.
A canceled or failed upload rejects with AxiosError. Progress availability depends on the
Axios adapter/runtime; browser XHR and Node HTTP support byte progress.
Uploading does not automatically change an exercise. Save the returned URL in the question's
media array, an option's image_url/media field, the homework item's media array, or the
course's image field using its create/update operation. Preserve the other media entries and
option values when editing. Returned image URLs are service-relative, so resolve them against
your configured LMS service origin for display. This endpoint currently requires
lms:course:update, including when the image will be used by an exam or homework.
