按源仓库内容呈现,保留标题、案例、代码、表格、链接以及原文引用的演示图片。
Optimize Loader
Anti-patterns to catch
1. Re-fetching data the caller already has
If a loader fetches a record (e.g. getVideoWithClipsById) and then passes just the ID to a downstream function that re-fetches the same record internally — change the downstream function's signature to accept the already-fetched object.
// BAD — getNextVideoId internally calls getVideoWithClipsById again
const video = yield * db.getVideoWithClipsById(videoId);
const nextId = yield * db.getNextVideoId(videoId);
// GOOD — pass the already-fetched video
const video = yield * db.getVideoWithClipsById(videoId);
const nextId = yield * db.getNextVideoId(video);When refactoring signatures, type the parameter as the minimal shape needed, not the full return type:
// Accept only what the function actually reads
function* (currentVideo: {
id: string;
lesson: {
id: string;
videos: Array<{ id: string; path: string }>;
section: { repoVersion: { repo: { id: string } } };
} | null;
})2. Over-fetching nested relations
If a function only needs IDs and paths for navigation but loads full nested trees including clips, transcripts, etc. — create a slim query variant.
// BAD — loads clips, chapters, thumbnails etc. just to get video IDs
const course = yield * getCourseWithSectionsById(repoId);
// GOOD — dedicated lightweight query
const course = yield * getCourseNavigationData(repoId);Slim query checklist:
columns: { id: true, path: true }— only select needed columns on leaf relationslimit: 1on relations like versions where you only need the latest- Omit
with:for relations you don't traverse (clips, chapters, thumbnails) - Keep
where:filters (e.g.archived = false) andorderByintact
3. Sequential independent queries
If a loader runs multiple independent DB calls sequentially, parallelize with Effect.all:
// BAD — sequential, total time = sum of both
const nextVideoId = yield * db.getNextVideoId(video);
const previousVideoId = yield * db.getPreviousVideoId(video);
// GOOD — parallel, total time = max of both
const [nextVideoId, previousVideoId] =
yield * Effect.all([db.getNextVideoId(video), db.getPreviousVideoId(video)]);Dependency injection pattern
When adding a new query (like getCourseNavigationData) that lives in course operations but is used by video operations:
- Add the query to
db-course-operations.server.tsand export it - Declare the cross-domain dependency in
VideoOperationsService'seffectblock viayield* CourseOperationsService - The Effect Layer system resolves cross-domain dependencies automatically

