bsuir-iis-api
v0.9.1
Published
Type-safe ESM SDK for BSUIR IIS API
Maintainers
Readme
bsuir-iis-api
Type-safe ESM SDK for BSUIR IIS API with support for Node.js and browser runtimes. Example project exist in this repo.
Runtime requirements
- Node.js
>=20as declared inpackage.json(engines). CI runs on Node 20, 22, and 24. - A global
fetchimplementation, or passfetchintocreateBsuirClient({ fetch })(for tests or polyfills). AbortController/AbortSignalfor cancellation and timeouts. WhenAbortSignal.anyis available (current Node and modern browsers), the client combines the per-request timeout with yoursignalusing the platform API; otherwise it merges them manually withsetTimeout, so timeouts still apply together with a caller-providedAbortSignal.
Install
npm install bsuir-iis-apiQuick start
Default schedule calls return a normalized payload (defaultRaw: false). That shape includes lessons, lessonsByDay, scheduleLessons, and examLessons (see types). With { raw: true } you get the API’s raw ScheduleResponse instead—no lessons field; use schedules / exams and match examples to the option you use.
import { createBsuirClient } from "bsuir-iis-api";
const client = createBsuirClient();
const schedule = await client.schedule.getGroup("053503");
// Normalized: `lessons` = weekly + exams flattened; see `scheduleLessons` / `examLessons` to split.
console.log(schedule.lessons.length);Client options
const client = createBsuirClient({
baseUrl: "https://iis.bsuir.by/api/v1",
timeoutMs: 10000,
retries: 2,
retryDelayMs: 300,
retryMaxDelayMs: 3000,
retryJitter: true,
cache: { ttlMs: 60_000, maxEntries: 200 },
dedupeInFlight: true,
validateResponses: false,
hooks: {
onRetry: ({ endpoint, delayMs, reason }) => {
console.log("retry", endpoint, delayMs, reason);
}
},
defaultRaw: false
});fetchcan be passed for custom runtime/testing.signalincreateBsuirClient({ signal })acts as a global cancellation signal for all requests made by that client.cachestores successful GET responses in-memory for the configured TTL.dedupeInFlightreuses the same in-flight GET request for concurrent callers (when no per-request signal is passed).validateResponsesenables runtime payload-shape checks for key endpoints.hooksprovides lifecycle callbacks (onRequest,onRetry,onResponse,onError) for observability.AbortSignalis supported by all read methods.
API
Schedule
client.schedule.getGroup(groupNumber, options?)client.schedule.getEmployee(urlId, options?)client.schedule.getGroupFiltered(groupNumber, filter, options?)client.schedule.getEmployeeFiltered(urlId, filter, options?)client.schedule.getGroupExams(groupNumber, options?)client.schedule.getEmployeeExams(urlId, options?)client.schedule.getGroupBySubgroup(groupNumber, subgroup, options?)client.schedule.getEmployeeBySubgroup(urlId, subgroup, options?)client.schedule.getCurrentWeek(options?)client.schedule.getLastUpdateByGroup({ groupNumber } | { id }, options?)client.schedule.getLastUpdateByEmployee({ urlId } | { id }, options?)
Last update (legacy IIS). The upstream routes /last-update-date/student-group and /last-update-date/employee are legacy on the BSUIR IIS side and are no longer maintained. For newer group identifiers (six-digit numbers such as 524404), the student-group endpoint may respond with an error; do not rely on these calls for cache freshness or invalidation for such groups.
Catalogs
client.groups.listAll(options?)client.employees.listAll(options?)client.faculties.listAll(options?)client.departments.listAll(options?)client.specialities.listAll(options?)client.auditories.listAll(options?)
Announcements
client.announcements.byEmployee(urlId, options?)client.announcements.byDepartment(id, options?)
When IIS responds with HTTP 404 or 400 (no list, missing resource, or endpoint quirks), these methods resolve to an empty array [] instead of throwing BsuirApiError. Client-side validation still runs first (urlId, department id). If IIS later returns a meaningful 400 for bad parameters, it will also map to []; other HTTP errors are unchanged.
Errors
SDK throws typed errors:
BsuirApiErrorfor HTTP errors (containsstatus,endpoint,body). Exception:client.announcements.byEmployee/byDepartmentresolve to[]on IIS HTTP404or400instead of throwing (see Announcements above).BsuirNetworkErrorfor transport errors (containsendpointand standardcause)BsuirResponseValidationErrorfor invalid payload shapes whenvalidateResponses: trueBsuirTimeoutErrorfor timeouts (containsendpoint,timeoutMs)BsuirValidationErrorfor invalid input parametersBsuirConfigurationErrorwhen the runtime has nofetchand none was passed tocreateBsuirClient({ fetch })
Validation rules:
groupNumbermust contain digits onlyurlIdmust be a slug with letters/digits/hyphens (for examples-nesterenkov)idandsubgroupparameters must be positive integers Retry and abort behavior:Retries are applied to
429,500,502,503,504Retry-Afteris respected for retriable responsesCaller-provided aborted
AbortSignalis re-thrown as nativeAbortErrorInternal timeout is mapped to
BsuirTimeoutError
createBsuirClient() throws BsuirConfigurationError if no fetch implementation is available.
Successful HTTP responses (body parsing)
For 2xx responses the client reads the body as text, then applies JSON.parse when the payload is valid JSON:
- Valid JSON is returned even when
Content-Typedoes not includeapplication/json(mislabeled responses still parse). - If
Content-Typeindicatesapplication/jsonbut the body is empty or not valid JSON, the client throwsBsuirApiError(Invalid JSON response payload), same as for a truncated{payload. - If the body is empty and the content type does not indicate JSON, the result is an empty string
""(analogous to reading plain text). Typical IIS catalog JSON endpoints return a non-empty body.
Raw vs normalized schedule response
By default, schedule methods return a normalized NormalizedScheduleResponse: lessons is all flattened items (weekly + exams), each tagged with source: "schedules" | "exams"; scheduleLessons / examLessons are the same rows split by source; lessonsByDay groups by weekday.
const raw = await client.schedule.getGroup("053503", { raw: true });Use defaultRaw: true in createBsuirClient to change global behavior.
When raw is omitted, getGroup() and getEmployee() return normalized payload.
In raw mode API may return schedules: null; normalized mode always converts it to {}.
In raw mode some lesson fields may also be nullable (weekNumber, lessonTypeAbbrev), so keep null checks if you consume raw payload directly.
README examples match the installed package version; if types and docs ever diverge, rely on NormalizedScheduleResponse / ScheduleResponse from the same release.
Current week
client.schedule.getCurrentWeek() returns the current week value directly from IIS API.
The SDK normalizes current-week payloads, including plain-text responses like 1\n.
Filtering example:
const exams = await client.schedule.getGroupFiltered("053503", {
source: "exams",
lessonTypeAbbrev: ["Консультация", "Экзамен"]
});const subgroupLessons = await client.schedule.getEmployeeBySubgroup("s-nesterenkov", 1);Development
npm install
npm run lint
npm run lint:fix
npm run check
npm run build
npm run api:reportnpm run build uses tsup with experimentalDts so .d.ts output is produced via @microsoft/api-extractor rather than the legacy Rollup declaration path (which is awkward with TypeScript 6’s baseUrl deprecation). TypeScript’s handbook notes that paths can be used without baseUrl when you need path mapping.
Linting uses ESLint flat config with strict type-aware TypeScript rules for src,
plus test-specific overrides for test and vitest.config.ts.
Live contract tests against real BSUIR API are opt-in:
BSUIR_LIVE_TESTS=1 npm run test:livePowerShell:
$env:BSUIR_LIVE_TESTS="1"; npm run test:liveCI has a manual workflow_dispatch path that also runs live contracts (live-contract job).
Release checklist
- Run
npm run check:full. - Update version and
CHANGELOG.mdin the same release commit. - Push to
mainto trigger GitHub Actions release workflow. - Verify published package in a clean project:
mkdir bsuir-iis-smoke && cd bsuir-iis-smoke
npm init -y
npm install bsuir-iis-api@latest
node -e "import('bsuir-iis-api').then(m=>console.log(typeof m.createBsuirClient))"The project keeps CHANGELOG.md manually curated for stable release notes.
License
MIT
