coursesOwned.
Request
{
studentById(studentId: "33333333-3333-4333-8333-333333333333") {
id fullName email
coursesOwned {
id name progress { percentage isCompleted }
modules { id name index contents { id name index variant status countsForCompletion isAccessible
variantData {
__typename
... on CourseModuleContentVideoLesson {
progress { percentage isCompleted maxSecond currentSecond }
}
... on CourseModuleContentQuiz {
progress { isCompleted attempt { score isPassed completedOn } }
}
} } }
}
}
}
{
"data": {
"studentById": {
"id": "33333333-3333-4333-8333-333333333333",
"fullName": "Jamie Smith",
"email": "student@example.com",
"coursesOwned": [
{
"id": "22222222-2222-4222-8222-222222222222",
"name": "Drawing Essentials",
"progress": {
"percentage": 0.5,
"isCompleted": false
},
"modules": [
{
"id": "66666666-6666-4666-8666-666666666666",
"name": "Foundations",
"index": 0,
"contents": [
{
"id": "77777777-7777-4777-8777-777777777777",
"name": "Drawing basic shapes",
"index": 0,
"variant": "videoLesson",
"status": "published",
"countsForCompletion": true,
"isAccessible": true,
"variantData": {
"__typename": "CourseModuleContentVideoLesson",
"progress": {
"percentage": 0.75,
"isCompleted": false,
"maxSecond": 90,
"currentSecond": 60
}
}
},
{
"id": "99999999-9999-4999-8999-999999999999",
"name": "Check your understanding",
"index": 1,
"variant": "quiz",
"status": "published",
"countsForCompletion": true,
"isAccessible": true,
"variantData": {
"__typename": "CourseModuleContentQuiz",
"progress": {
"isCompleted": true,
"attempt": {
"score": 100,
"isPassed": true,
"completedOn": 1788224400000
}
}
}
}
]
}
]
}
]
}
}
}
Use the result
const courseId = "22222222-2222-4222-8222-222222222222";
const course = response.data.studentById.coursesOwned.find(course => course.id === courseId);
const rows = course ? [...course.modules].sort((a, b) => a.index - b.index).flatMap(module =>
[...module.contents].sort((a, b) => a.index - b.index).map(content => {
const data = content.variantData;
return {
module: module.name, contentId: content.id, lesson: content.name,
variant: content.variant, status: content.status,
accessible: content.isAccessible, countsForCompletion: content.countsForCompletion,
isCompleted: data?.progress?.isCompleted ?? null,
videoPercent: data?.__typename === "CourseModuleContentVideoLesson" && data.progress
? Math.round(data.progress.percentage * 100) : null,
quizScore: data?.__typename === "CourseModuleContentQuiz"
? data.progress?.attempt?.score ?? null : null
};
})
) : [];
coursesOwned to get this Student’s progress; a top-level Course request uses the caller’s context. Administrator reads include draft content. Keep null progress separate from zero: an inaccessible video or quiz can return null. Video progress records the furthest playback position reached, rather than total watched time. Use these results to review certificate eligibility under your own criteria.
