GraphQL API Reference
The B12 SIS provides a read-only GraphQL API for flexible data querying. This API allows you to fetch exactly the data you need in a single request, with support for filtering, pagination, sorting, and nested relationships.
Overview
Feature |
Description |
|---|---|
Endpoint |
|
Methods |
POST, GET |
Authentication |
JWT Bearer token required |
Operations |
Query only (read-only API) |
Playground |
|
Note
The GraphQL API is read-only. All create, update, and delete operations should use the REST API.
Quick Start
1. Authentication
Include your JWT token and team ID in request headers:
curl -X POST https://api.yourschool.edu/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "X-Team-ID: YOUR_TEAM_ID" \
-d '{"query": "{ students { nodes { id firstName lastName } } }"}'
2. Basic Query
query {
students {
nodes {
id
code
firstName
lastName
email
status
}
pageInfo {
totalCount
totalPages
currentPage
hasNextPage
}
}
}
3. Query with Variables
query GetStudent($id: ID!) {
student(id: $id) {
id
firstName
lastName
email
currentClass {
name
code
}
}
}
Variables:
{
"id": "550e8400-e29b-41d4-a716-446655440000"
}
Request Headers
Header |
Required |
Description |
|---|---|---|
|
Yes |
JWT Bearer token: |
|
Yes |
Team/Campus UUID for multi-tenancy |
|
Yes |
Must be |
|
No |
Language preference ( |
Available Queries
Academic Structure
Query |
Single |
Description |
|---|---|---|
|
|
School years (e.g., 2024-2025) |
|
|
Terms within academic sessions |
|
|
Homeroom classes |
|
|
Subject courses |
|
|
Subject definitions |
|
|
Educational programs |
|
|
Grade levels (K-12) |
|
|
Education level groupings |
Personnel
Query |
Single |
Description |
|---|---|---|
|
|
Student records |
|
|
Teacher/staff records |
|
|
System user accounts |
|
|
Parent/guardian records |
Assessment & Grading
Query |
Single |
Description |
|---|---|---|
|
|
Semester gradebooks |
|
|
Course-level grades |
|
|
Assignments, tests, quizzes |
|
|
Student scores |
|
|
Grading scale definitions |
Attendance
Query |
Single |
Description |
|---|---|---|
|
|
Daily attendance records |
|
|
Attendance status configurations |
Enrollment
Query |
Single |
Description |
|---|---|---|
|
|
Class enrollments |
|
|
Course enrollments |
Timetabling
Query |
Single |
Description |
|---|---|---|
|
|
Timetable definitions |
|
|
Class periods |
|
|
Rooms/facilities |
Communication
Query |
Single |
Description |
|---|---|---|
|
|
Student behavior records |
|
|
Email templates |
System Configuration
Query |
Single |
Description |
|---|---|---|
|
|
User roles |
|
|
Teams/campuses |
|
|
Dynamic field definitions |
|
|
System audit logs |
Other
Query |
Single |
Description |
|---|---|---|
|
|
Campus facilities |
|
|
Holiday definitions |
|
|
Guardian-student relationships |
|
|
CRM leads |
Pagination
All list queries support pagination via the pagination argument.
Input
input PaginationInput {
page: Int = 1 # Page number (1-indexed)
limit: Int = 20 # Items per page (max: 100)
}
Example
query {
students(pagination: { page: 2, limit: 25 }) {
nodes {
id
firstName
lastName
}
pageInfo {
totalCount
totalPages
currentPage
hasNextPage
hasPreviousPage
}
}
}
Response
{
"data": {
"students": {
"nodes": [...],
"pageInfo": {
"totalCount": 150,
"totalPages": 6,
"currentPage": 2,
"hasNextPage": true,
"hasPreviousPage": true
}
}
}
}
Sorting
All list queries support sorting via the sort argument.
Input
input SortInput {
field: String! # Field name (camelCase)
order: SortOrder = ASC # ASC or DESC
}
Example
query {
students(
sort: { field: "createdAt", order: DESC }
pagination: { limit: 10 }
) {
nodes {
id
firstName
lastName
createdAt
}
}
}
Common Sort Fields
Field |
Description |
|---|---|
|
Record creation date (default) |
|
Last update date |
|
Name field |
|
Code field |
|
Status field |
Filtering
The GraphQL API supports complex filtering with AND/OR logic.
Filter Structure
input FilterGroup {
group: FilterGroupType! # AND or OR
conditions: [FilterCondition!]
groups: [FilterGroup!] # Nested groups
}
input FilterCondition {
field: String!
operator: FilterOperator!
value: String # Single value
values: [String!] # Multiple values (for IN, NOT_IN)
}
Available Operators
Operator |
Description |
Example |
|---|---|---|
|
Equal |
|
|
Not equal |
|
|
Greater than |
|
|
Greater than or equal |
|
|
Less than |
|
|
Less than or equal |
|
|
Contains substring |
|
|
Starts with |
|
|
Ends with |
|
|
In list |
|
|
Not in list |
|
|
Is null |
|
|
Is not null |
|
Filter Examples
Simple Filter
query {
students(
filter: {
group: AND
conditions: [
{ field: "status", operator: EQ, value: "active" }
]
}
) {
nodes {
id
firstName
lastName
status
}
}
}
Multiple Conditions (AND)
query {
students(
filter: {
group: AND
conditions: [
{ field: "status", operator: EQ, value: "active" }
{ field: "enrollmentYear", operator: GTE, value: "2020" }
]
}
) {
nodes {
id
firstName
enrollmentYear
}
}
}
OR Conditions
query {
students(
filter: {
group: OR
conditions: [
{ field: "status", operator: EQ, value: "active" }
{ field: "status", operator: EQ, value: "alumni" }
]
}
) {
nodes {
id
status
}
}
}
Using IN Operator
query {
students(
filter: {
group: AND
conditions: [
{ field: "status", operator: IN, values: ["active", "alumni"] }
]
}
) {
nodes {
id
status
}
}
}
Search by Name (CONTAINS)
query {
students(
filter: {
group: AND
conditions: [
{ field: "firstName", operator: CONTAINS, value: "john" }
]
}
) {
nodes {
id
firstName
lastName
}
}
}
Nested AND/OR Groups
query {
students(
filter: {
group: AND
conditions: [
{ field: "status", operator: EQ, value: "active" }
]
groups: [
{
group: OR
conditions: [
{ field: "enrollmentYear", operator: EQ, value: "2023" }
{ field: "enrollmentYear", operator: EQ, value: "2024" }
]
}
]
}
) {
nodes {
id
firstName
enrollmentYear
}
}
}
Entity Schemas
Student
type Student {
# Identity
id: ID!
code: String!
moetId: String
status: String!
# Personal Info
firstName: String
lastName: String
fullNameLastFirst: String
fullNameFirstLast: String
email: String
phone: String
avatar: String
gender: String
religion: String
personalIdNumber: String
birthDate: Date
nationality: String
ethnicity: String
# Enrollment
enrollmentDate: Date
enrollmentYear: Int
source: String
# App/LMS Access
appActive: Boolean
appUser: String
appLastLogin: Time
lmsActive: Boolean
lmsId: String
lmsUser: String
# Addresses
permanentAddress: Address
currentAddress: Address
birthPlaceAddress: Address
birthRegisAddress: Address
# Relationships
currentClass: Class
currentClassId: ID
program: Program
programId: ID
user: User
userId: ID
# Collections
behaviors: [Behavior!]!
attendances: [Attendance!]!
gradebooks: [Gradebook!]!
studentEnrollClasses: [StudentEnrollClass!]!
studentEnrollCourses: [StudentEnrollCourse!]!
guardians: [PersonRelationship!]!
# Metadata
createdAt: Time
updatedAt: Time
teamId: ID
createdBy: User
updatedBy: User
team: Team
}
Teacher
type Teacher {
# Identity
id: ID!
code: String!
type: String!
status: String!
# Personal Info
firstName: String
lastName: String
fullNameLastFirst: String
fullNameFirstLast: String
email: String
phone: String
avatar: String
gender: String
religion: String
personalIdNumber: String
birthDate: Date
nationality: String
ethnicity: String
# Addresses
permanentAddress: Address
currentAddress: Address
# Relationships
user: User
userId: ID
# Collections
workExperiences: [WorkExperience!]!
certifications: [Certification!]!
educationQualifications: [EducationQualification!]!
classes: [Class!]!
# Metadata
createdAt: Time
updatedAt: Time
teamId: ID
createdBy: User
updatedBy: User
team: Team
}
Class
type Class {
id: ID!
name: String
description: String
code: String!
status: String!
# Relationships
academicSession: AcademicSession
academicSessionId: ID
gradeLevel: GradeLevel
gradeLevelId: ID
program: Program
programId: ID
facility: Facility
facilityId: ID
primaryTeacher: Teacher
primaryTeacherId: ID
teacherAssistants: [Teacher!]!
studentEnrollClasses: [StudentEnrollClass!]!
timetables: [Timetable!]!
# Computed
studentCount: Int
# Metadata
createdAt: Time
updatedAt: Time
teamId: ID
createdBy: User
updatedBy: User
team: Team
}
Course
type Course {
id: ID!
name: String
description: String
code: String!
shortName: String
status: String!
type: String!
# Canvas Integration
canvasId: String
needCanvasSync: Boolean
# Relationships
academicSession: AcademicSession
academicSessionId: ID
program: Program
programId: ID
educationLevel: EducationLevel
educationLevelId: ID
subject: Subject
subjectId: ID
semester: Semester
semesterId: ID
primaryTeacher: Teacher
primaryTeacherId: ID
teacherAssistants: [Teacher!]!
studentEnrollCourses: [StudentEnrollCourse!]!
assessments: [Assessment!]!
# Computed
studentCount: Int
# Metadata
createdAt: Time
updatedAt: Time
teamId: ID
createdBy: User
updatedBy: User
team: Team
}
Assessment
type Assessment {
id: ID!
name: String
description: String
title: String!
type: String!
category: String
weight: Float
totalPoints: Float!
dueDate: Time
assessmentDate: Time
isPublished: Boolean!
allowLateSubmission: Boolean
latePenalty: Float
instructions: String
rubric: JSON
isLocked: Boolean!
noShowReport: Boolean
# Canvas Integration
canvasAssignmentId: String
canvasAssignmentGroupId: String
canvasAssignmentGroupName: String
canvasGroupWeight: Float
omitFromFinalGrade: Boolean
# Relationships
course: Course
courseId: ID
gradeScale: GradeScale
gradeScaleId: ID
assessmentResults: [AssessmentResult!]!
# Metadata
createdAt: Time
updatedAt: Time
teamId: ID
createdBy: User
updatedBy: User
team: Team
}
Gradebook
type Gradebook {
id: ID!
name: String
description: String
overallScore: Float
overallGrade: String
gpa: Float
totalCredits: Float
earnedCredits: Float
status: String!
isPublished: Boolean!
publishedAt: Time
comments: String
lastCalculatedAt: Time
lastGenReportAt: Time
reportCardFile: String
isLocked: Boolean!
# Relationships
student: Student
studentId: ID
semester: Semester
semesterId: ID
academicSession: AcademicSession
academicSessionId: ID
gradebookCourses: [GradebookCourse!]!
# Metadata
createdAt: Time
updatedAt: Time
teamId: ID
createdBy: User
updatedBy: User
team: Team
}
Behavior
type Behavior {
id: ID!
name: String
description: String
type: String!
category: String
points: Int
date: Date
time: Time
notes: String
isNotified: Boolean
notifiedAt: Time
# Relationships
student: Student
studentId: ID
students: [Student!]! # Many-to-many for group behaviors
academicSession: AcademicSession
academicSessionId: ID
class: Class
classId: ID
emailTemplate: EmailTemplate
emailTemplateId: ID
# Metadata
createdAt: Time
updatedAt: Time
teamId: ID
createdBy: User
updatedBy: User
team: Team
}
Complex Query Examples
Dashboard Statistics
query Dashboard {
students(filter: { group: AND, conditions: [{ field: "status", operator: EQ, value: "active" }] }) {
pageInfo { totalCount }
}
teachers(filter: { group: AND, conditions: [{ field: "status", operator: EQ, value: "working" }] }) {
pageInfo { totalCount }
}
classes {
pageInfo { totalCount }
}
courses {
pageInfo { totalCount }
}
}
Student with Full Details
query GetStudentDetails($id: ID!) {
student(id: $id) {
id
code
firstName
lastName
email
phone
status
enrollmentYear
birthDate
gender
currentClass {
id
name
code
gradeLevel {
name
}
}
program {
id
name
}
guardians {
id
relationship
isPrimary
}
gradebooks {
id
overallScore
overallGrade
semester {
name
}
}
}
}
Class Roster with Students
query GetClassRoster($classId: ID!) {
class(id: $classId) {
id
name
code
primaryTeacher {
firstName
lastName
}
gradeLevel {
name
}
studentEnrollClasses {
student {
id
code
firstName
lastName
email
status
}
enrollmentDate
status
}
}
}
Course with Assessments and Results
query GetCourseAssessments($courseId: ID!) {
course(id: $courseId) {
id
name
code
primaryTeacher {
firstName
lastName
}
assessments {
id
title
type
totalPoints
dueDate
isPublished
assessmentResults {
student {
id
firstName
lastName
}
score
grade
submittedAt
}
}
}
}
Teachers with Their Classes and Courses
query GetTeacherSchedule($teacherId: ID!) {
teacher(id: $teacherId) {
id
firstName
lastName
email
classes {
id
name
code
gradeLevel {
name
}
studentCount
}
}
courses(
filter: {
group: AND
conditions: [
{ field: "primaryTeacherId", operator: EQ, value: $teacherId }
]
}
) {
nodes {
id
name
code
subject {
name
}
studentCount
}
}
}
Attendance Report
query GetAttendanceReport($classId: ID!, $startDate: String!, $endDate: String!) {
attendances(
filter: {
group: AND
conditions: [
{ field: "classId", operator: EQ, value: $classId }
{ field: "date", operator: GTE, value: $startDate }
{ field: "date", operator: LTE, value: $endDate }
]
}
sort: { field: "date", order: ASC }
) {
nodes {
id
date
status
checkInTime
checkOutTime
notes
student {
id
code
firstName
lastName
}
}
pageInfo {
totalCount
}
}
}
Gradebook Report
query GetGradebookReport($semesterId: ID!) {
gradebooks(
filter: {
group: AND
conditions: [
{ field: "semesterId", operator: EQ, value: $semesterId }
{ field: "isPublished", operator: EQ, value: "true" }
]
}
sort: { field: "overallScore", order: DESC }
) {
nodes {
id
overallScore
overallGrade
gpa
student {
id
code
firstName
lastName
currentClass {
name
}
}
gradebookCourses {
score
grade
course {
name
subject {
name
}
}
}
}
pageInfo {
totalCount
}
}
}
Using GraphQL Clients
JavaScript (fetch)
async function graphqlQuery(query, variables = {}) {
const response = await fetch('/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'X-Team-ID': localStorage.getItem('teamId')
},
body: JSON.stringify({ query, variables })
});
const result = await response.json();
if (result.errors) {
throw new Error(result.errors[0].message);
}
return result.data;
}
// Usage
const data = await graphqlQuery(`
query GetStudents($limit: Int!) {
students(pagination: { limit: $limit }) {
nodes {
id
firstName
lastName
}
pageInfo {
totalCount
}
}
}
`, { limit: 10 });
Angular (Apollo Angular)
import { Apollo, gql } from 'apollo-angular';
@Injectable()
export class StudentService {
constructor(private apollo: Apollo) {}
getStudents(limit: number) {
return this.apollo.watchQuery({
query: gql`
query GetStudents($limit: Int!) {
students(pagination: { limit: $limit }) {
nodes {
id
firstName
lastName
}
pageInfo {
totalCount
}
}
}
`,
variables: { limit }
}).valueChanges;
}
}
cURL
# Simple query
curl -X POST https://api.yourschool.edu/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Team-ID: YOUR_TEAM_ID" \
-d '{"query": "{ students(pagination: { limit: 5 }) { nodes { id firstName lastName } } }"}'
# Query with variables
curl -X POST https://api.yourschool.edu/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Team-ID: YOUR_TEAM_ID" \
-d '{
"query": "query GetStudent($id: ID!) { student(id: $id) { id firstName lastName email } }",
"variables": {"id": "550e8400-e29b-41d4-a716-446655440000"}
}'
GraphQL Playground
Access the interactive GraphQL IDE at /playground (development mode only).
Features
Auto-complete for queries
Schema documentation browser
Query history
Variables panel
Headers configuration
Enabling Playground
Set DEV_MODE=true in your environment to enable the playground.
Warning
The playground is disabled in production for security reasons.
Error Handling
Error Response Format
{
"errors": [
{
"message": "record not found",
"path": ["student"],
"extensions": {
"code": "NOT_FOUND"
}
}
],
"data": {
"student": null
}
}
Common Error Codes
Code |
Description |
|---|---|
|
Missing or invalid JWT token |
|
Insufficient permissions |
|
Resource not found |
|
Invalid query or variables |
|
Server error |
Best Practices
1. Request Only What You Need
# Good - Only request needed fields
query {
students {
nodes {
id
firstName
lastName
}
}
}
# Avoid - Requesting all fields unnecessarily
query {
students {
nodes {
id
code
firstName
lastName
email
phone
avatar
# ... many more fields
}
}
}
2. Use Pagination for Large Datasets
query {
students(pagination: { page: 1, limit: 50 }) {
nodes { id firstName lastName }
pageInfo { totalCount hasNextPage }
}
}
3. Use Variables for Dynamic Values
# Good - Using variables
query GetStudentsByStatus($status: String!) {
students(
filter: {
group: AND
conditions: [{ field: "status", operator: EQ, value: $status }]
}
) {
nodes { id firstName }
}
}
5. Use Fragments for Reusable Fields
fragment StudentBasicInfo on Student {
id
code
firstName
lastName
email
status
}
query {
students {
nodes {
...StudentBasicInfo
}
}
}
Rate Limiting
The GraphQL API shares rate limits with the REST API:
100 requests per minute per user
1000 requests per hour per user
Exceeding limits returns a 429 Too Many Requests response.
Multi-tenancy
Data is automatically filtered by team:
X-Team-IDheader identifies the current teamAll queries filter by team and child teams
Users only see data they have permission to access
Comparison: GraphQL vs REST API
Feature |
GraphQL |
REST API |
|---|---|---|
Use Case |
Flexible data fetching |
CRUD operations |
Operations |
Read-only (Query) |
Create, Read, Update, Delete |
Response |
Exactly what you request |
Fixed structure |
Multiple Resources |
Single request |
Multiple requests |
Filtering |
Built-in with operators |
POST body filter |
Nested Data |
Native support |
Preloads parameter |
Recommendation: Use GraphQL for complex read queries and dashboards. Use REST API for all write operations.