Syllabi Feature Analysis
Backend Implementation¶
Route Registration¶
In apps/backend/src/app.ts, two routes are registered for syllabi:
Database Interaction¶
The syllabi.ts controller interacts with what appears to be a Prisma ORM client (db) to query the database.
Endpoint 1: GET /syllabi¶
Request Processing¶
- Parameter Extraction:
-
Converts single course number or array of numbers into a consistent array format
-
Logging:
-
Logs the requested course numbers for debugging
-
Database Query:
- Uses Prisma's
findManyto retrieve matching syllabi - Filters by course numbers using an
inclause -
Selects only the necessary fields
-
Error Handling:
- If an error occurs, it's logged
- Returns an empty array instead of an error response
-
This provides graceful degradation for the frontend
-
Response:
- Logs the number of syllabi found
- Returns the syllabi array as JSON
Endpoint 2: GET /syllabi/all¶
Caching Mechanism¶
- Simple in-memory cache with timestampCache Invalidation Logic¶
if (new Date().valueOf() - allSyllabiEntry.lastCached.valueOf() > 1000 * 60 * 60 * 24) {
// Cache is expired (older than 24 hours)
}
Data Retrieval¶
const syllabiFromDB = await db.syllabi.findMany({
where: filter,
select: getAllSyllabiDbQuery.select,
});
const syllabi: Syllabus[] = syllabiFromDB.map((s) => ({
id: s.id,
season: s.season ?? "",
year: s.year ?? 0,
number: s.number ?? "",
url: s.url ?? null,
}));
Frontend Implementation¶
API Client (apps/frontend/src/app/api/syllabi.ts)¶
Core Data Fetching Function¶
const fetchSyllabi = async (numbers: string[]): Promise<Syllabus[]> => {
try {
const url = `${process.env.NEXT_PUBLIC_BACKEND_URL || ""}/syllabi`;
console.log("Original numbers (with types):", numbers.map(n => `${n} (${typeof n})`));
// Ensure numbers are always strings and padded to 5 digits
const paddedNumbers = numbers.map(num => String(num).padStart(5, '0'));
console.log("Padded numbers:", paddedNumbers);
const params = new URLSearchParams(
paddedNumbers.map((number) => ["number", number])
);
const response = await axios.get(url, {
headers: {"Content-Type": "application/json"},
params,
});
return response.data;
} catch (error) {
console.error("Error fetching syllabi:", error);
return [];
}
}
React Query Integration¶
Single Course Hook¶
export const useFetchSyllabus = (number: string) => {
return useQuery({
queryKey: ["syllabus", { number }],
queryFn: () => fetchSyllabusBatcher.fetch(number),
staleTime: STALE_TIME,
enabled: !!number,
});
};
Multiple Courses Hook¶
export const useFetchMultipleSyllabi = (numbers: string[]) => {
return useQueries({
queries: numbers.map((number) => ({
queryKey: ["syllabus", { number }],
queryFn: () => fetchSyllabusBatcher.fetch(number),
staleTime: STALE_TIME,
enabled: !!number,
})),
combine: (result) => {
return result.reduce((acc, { data }) => {
if (data) acc.push(data);
return acc;
}, [] as Syllabus[]);
},
});
};
Batching Mechanism¶
const fetchSyllabusBatcher = create({
fetcher: async (syllabusNumbers: string[]): Promise<Syllabus[]> => {
try {
const url = `${process.env.NEXT_PUBLIC_BACKEND_URL || ""}/syllabi`;
const params = new URLSearchParams(
syllabusNumbers.map((number) => ["number", number])
);
const response = await axios.get(url, {
headers: {
"Content-Type": "application/json",
},
params,
});
return response.data;
} catch (error) {
console.error("Error in syllabus batcher:", error);
return [];
}
},
resolver: keyResolver("number"),
scheduler: windowScheduler(10),
});
Complete Data Flow (End-to-End)¶
- User Interaction:
- A user views a course that has an associated syllabus
-
Or a user searches for specific course syllabi
-
Component Rendering:
-
A React component calls
useFetchSyllabus("15112")or similar -
React Query Processing:
- React Query checks its cache for ["syllabus", {number: "15112"}]
- If found and not stale (< 24 hours old), returns cached data
-
If not found or stale, proceeds with the request
-
Request Batching:
- The request enters the batching queue
- If other requests arrive within 10ms window, they're batched together
-
This creates a single request for multiple course numbers
-
HTTP Request:
- Axios sends a GET request to
/syllabi?number=15112 -
Or for batched requests:
/syllabi?number=15112&number=15122&number=15150 -
Backend Processing:
- Express server receives the request
- Routes it to the
getSyllabihandler -
Extracts course numbers from query params
-
Database Query:
- Backend queries the database for matching syllabi
-
Selects only necessary fields (id, season, year, number, section, url)
-
Response Generation:
- Backend formats the database results
-
Returns JSON array of matching syllabi
-
Frontend Processing:
- Axios receives the HTTP response
- React Query stores the data in cache (valid for 24 hours)
-
Component receives the data and renders accordingly
-
User Experience:
- User sees syllabus information for the course(s)
- Potentially clicks on the syllabus URL to view the full document
This comprehensive flow demonstrates how the syllabi feature efficiently retrieves and displays syllabi information through a well-designed data pipeline.