Courses
Courses, lectures, and enrollments — a lightweight LMS surface.
- Use it for: Public website · Server integration
- Reads: public token — works in the browser; Bearer for
getEnrollment,listEnrollments - Writes: Bearer (
apiKey) — server-side only - CLI:
uidu courses - Auth: what each token can do and where it may run — Authentication
A lightweight LMS: courses made of ordered lectures, and the people enrolled in them. Courses and lectures are public content you can render on a website; enrollments are per-person data you read from your server.
Mental model
Course ← listCourses / getCourse (public token)
├─ name, abstract, cover, kind, mode, tags, publicPath
├─ body rich text — getCourse only
└─→ Lecture ← listLectures / getLecture (public token)
├─ position order within the course
├─ duration minutes
├─ link external URL, e.g. the video host
├─ body rich text — getLecture only
└─ previousLecture / nextLecture { id, name, position } | null
Enrollment ← listEnrollments / getEnrollment (Bearer)
├─ course, contact { id, name } / { id, name, email }
├─ currentLecture where the person is
├─ aasmState
└─ fieldValuesByShortname custom fields — getEnrollment onlykindandmodeare strings from the course's settings:kindise_learning,iltorvilt;modeisexternalorinternal.tagsis free-form.- Lists are summaries.
listCourseshas nobody;listLectureshas nobodyand no previous/next. Fetch one record withgetCourse/getLecturefor those. - Lectures are listed workspace-wide.
listLectureshas no course argument: it returns lectures across every course, and you keep the ones whosecourse.idmatches. It also returns one page, so a course whose lectures fall past that page comes back short — walk every page withpaginatewhen you need a full course (see the first use case). orderBy,scopes,from/toare passed through tolistLecturesas strings;orderBy: 'position'gives course order.listCoursesandlistEnrollmentstake searchparams(SearchParams:filters,sorting,options) instead.- Enrollments are private. They tie a contact to a course, so reading them needs the
account Bearer (
apiKey) on your server. With a public token the list comes back empty rather than failing.aasmStateis the enrollment's state as a string; read it, don't hard-code a fixed set of values. - Writes (
createCourse,updateCourse,deleteCourse) take{ input: { attributes } }(plusidto update or delete), need the Bearer, and return the payload witherrors— see Writes. Lectures and enrollments have no writes in the SDK yet.
Use cases
The examples import uidu from lib/uidu.ts, a client built with publicToken and apiKey
and imported only from server code, as in Authentication.
Course page with its lectures
Goal: a course's page with its description and the ordered list of its lectures.
// app/courses/[id]/page.tsx
import { getCourse, paginate, ListLecturesDocument } from '@uidu/client';
import { RichText } from '@uidu/react';
import { notFound } from 'next/navigation';
import { uidu } from '@/lib/uidu';
export default async function CoursePage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const [course, lectures] = await Promise.all([
getCourse(uidu, { id: decodeURIComponent(id) }),
// Every lecture in the workspace, every page — then keep this course's.
paginate(uidu, ListLecturesDocument, {
variables: { first: 100, orderBy: 'position' },
extract: (data) => data.currentWorkspace?.lectures,
}),
]);
if (!course) notFound();
const courseLectures = lectures
.filter((l) => l.course?.id === course.id)
.sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
return (
<article>
<h1>{course.name ?? 'Untitled course'}</h1>
<p>{course.abstract}</p>
{course.body && <RichText doc={course.body} />}
<ol>
{courseLectures.map((lec) => (
<li key={lec.id}>
<a href={`/lectures/${encodeURIComponent(lec.id)}`}>{lec.name}</a>
{lec.duration && ` (${lec.duration} min)`}
</li>
))}
</ol>
</article>
);
}- For a catalogue page,
listCourses(uidu, { first: 20 })returns the course summaries:name,abstract,cover,kind,mode,tags,publicPath(e.g./courses/intro-to-x) andpublicUrl. listLectures(uidu, { first: 100, orderBy: 'position' })is enough when the workspace has few lectures;paginateis what makes the list complete. More in Pagination & field values.- The sort after filtering keeps the order right even when lectures of different courses interleave across pages.
Lecture page with previous / next
Goal: one lecture's content, with links to the lectures before and after it.
// app/lectures/[id]/page.tsx
import { getLecture } from '@uidu/client';
import { RichText } from '@uidu/react';
import { notFound } from 'next/navigation';
import { uidu } from '@/lib/uidu';
const href = (id: string) => `/lectures/${encodeURIComponent(id)}`;
export default async function LecturePage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const lecture = await getLecture(uidu, { id: decodeURIComponent(id) });
if (!lecture) notFound();
const { course, previousLecture: prev, nextLecture: next } = lecture;
return (
<article>
{course && (
<a href={`/courses/${encodeURIComponent(course.id)}`}>{course.name}</a>
)}
<h1>{lecture.name}</h1>
{lecture.duration && <p>{lecture.duration} min</p>}
{lecture.link && <a href={lecture.link}>Watch</a>}
{lecture.body && <RichText doc={lecture.body} />}
<nav>
{prev && <a href={href(prev.id)}>← {prev.name}</a>}
{next && <a href={href(next.id)}>{next.name} →</a>}
</nav>
</article>
);
}previousLecture/nextLectureare{ id, name, position } | null: no need to fetch the whole course to navigate.getLecturealso returnskind,cover,startDate/startTime(for scheduled lectures),publishedanddraft.
Enrollment report
Goal: from your server, list who is enrolled in which course and where they are, and read one enrollment's custom fields.
// app/api/enrollments/route.ts — server only, needs the apiKey client
import { listEnrollments, getEnrollment } from '@uidu/client';
import { uidu } from '@/lib/uidu';
export async function GET() {
const enrollments = await listEnrollments(uidu, { first: 50 });
const rows = enrollments.map((e) => ({
course: e.course.name,
person: e.contact.name,
email: e.contact.email,
state: e.aasmState,
at: e.currentLecture?.name ?? null,
}));
// One enrollment in full: its custom fields as a { shortname: value } map.
const first =
enrollments[0] && (await getEnrollment(uidu, { id: enrollments[0].id }));
return Response.json({ rows, fields: first?.fieldValuesByShortname ?? null });
}- Empty with no error means the client has no
apiKey: enrollments are invisible to a public token. fieldValuesByShortname(custom fields) andcurrentLecture.positionare ongetEnrollmentonly; the list hascurrentLecture { id, name }.- To read every enrollment, pass
ListEnrollmentsDocumenttopaginate.
API at a glance
Every @uidu/client function in Courses, generated from the source — the full reference has the rest of the SDK.
| Function | Kind | Auth | CLI | Description |
|---|---|---|---|---|
createCourse | write | Bearer | courses create | Create a course from { input: { attributes } }. Returns the payload (with errors) or null. |
deleteCourse | write | Bearer | courses delete | Delete a course by { input: { id } }. Returns the payload (with errors) or null. |
getCourse | read | public token | courses get | One course with its rich-text body, or null. |
getEnrollment | read | Bearer | — | One enrollment with its course, contact and custom fields (fieldValuesByShortname), or null. |
getLecture | read | public token | — | One lecture with its body, its course and its nextLecture / previousLecture for navigation, or null. |
listCourses | read | public token | courses list | The workspace's courses as a flat array, optionally filtered with search params; page with first/after. |
listEnrollments | read | Bearer | — | Course enrollments as a flat array, optionally filtered with search params; page with first/after. |
listLectures | read | public token | — | Lectures across every course as a flat array (filter by course.id yourself), with scopes, orderBy, from/to; page with first/after. |
updateCourse | write | Bearer | courses update | Update a course: { input: { id, attributes } }. Returns the payload (with errors) or null. |
Types: Course, Enrollment, Lecture
CLI:
| Entity | list | get | create | update | delete |
|---|---|---|---|---|---|
courses | ✓ | ✓ | ✓ | ✓ | ✓ |