Lazy‑Loading Reasoning Chunks in Documedic CDSS
Documedic is a clinical decision support system (CDSS) that delivers AI‑generated recommendations to clinicians through a web interface built with NestJS, React, PostgreSQL, and the OpenRouter API. One of the most visible performance bottlenecks in a CDSS is the size of the reasoning data that accompanies each recommendation. The reasoning is a multi‑paragraph explanation of how the AI arrived at a decision, and it is often several kilobytes long. When every recommendation is loaded eagerly, the initial bundle size grows, the page takes longer to render, and the user experience suffers, especially on mobile devices. This is where cdss pays off in practice.
The Problem
- Large payloads – A typical reasoning chunk can be 5–10 KB of plain text. When a clinician opens a patient case, dozens of recommendations may be displayed, each with its own reasoning.
- Slow initial render – React must download and parse all of the reasoning text before it can paint the UI. On a 3G connection, this can add several seconds to the load time.
- Resource waste – Most clinicians read only a handful of recommendations. Loading all reasoning data upfront means the browser downloads data that may never be viewed.
The Solution: Lazy‑Loading Reasoning Chunks
Documedic solves this problem by deferring the download of reasoning until it is needed. The approach is a three‑layer architecture:
- Backend API – A NestJS controller receives a request for a specific recommendation ID. It calls OpenRouter to generate the reasoning, stores the result in PostgreSQL, and returns a JSON payload containing only the recommendation text.
-
Frontend Lazy Component – The React app uses
React.lazyandSuspenseto load aReasoningChunkcomponent on demand. The component fetches the reasoning from the backend when it becomes visible. -
IntersectionObserver Trigger – An observer watches the
ReasoningChunkplaceholder. When the user scrolls near the chunk, the observer triggers the fetch, ensuring the network request is made only when the user is likely to read the content.
Backend Implementation
The NestJS controller is minimal. It receives the recommendation ID, calls the OpenRouter client, writes the reasoning to PostgreSQL, and returns the recommendation text. Getting cdss right early saves rework later.
// src/recommendations/recommendations.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { RecommendationsService } from './recommendations.service';
@Controller('recommendations')
export class RecommendationsController {
constructor(private readonly service: RecommendationsService) {}
@Get(':id')
async getRecommendation(@Param('id') id: string) {
const rec = await this.service.getRecommendation(id);
return { text: rec.text };
}
}
The service layer handles the OpenRouter call and database interaction.
// src/recommendations/recommendations.service.ts
import { Injectable } from '@nestjs/common';
import { OpenRouterClient } from '@openrouter/client';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
export class RecommendationsService {
private readonly openRouter = new OpenRouterClient({ apiKey: process.env.OPENROUTER_KEY });
constructor(private readonly prisma: PrismaService) {}
async getRecommendation(id: string) {
// Retrieve cached reasoning if present
const cached = await this.prisma.recommendation.findUnique({ where: { id } });
if (cached) return cached;
// Generate reasoning via OpenRouter
const { text } = await this.openRouter.generate({ prompt: `Explain recommendation ${id}` });
// Persist to PostgreSQL
return this.prisma.recommendation.create({ data: { id, text } });
}
}
The Prisma schema defines a simple table:
model Recommendation {
id String @id
text String
}
Frontend Implementation
On the client side, the Recommendation component renders the recommendation text and a lazy‑loaded placeholder for the reasoning.
// src/components/Recommendation.tsx
import React, { useRef, useEffect, useState, Suspense } from 'react';
const ReasoningChunk = React.lazy(() => import('./ReasoningChunk'));
export const Recommendation: React.FC<{ id: string; text: string }> = ({ id, text }) => {
const placeholderRef = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) setVisible(true);
},
{ rootMargin: '200px' },
);
if (placeholderRef.current) observer.observe(placeholderRef.current);
return () => observer.disconnect();
}, []);
return (
<div className="recommendation">
<p>{text}</p>
<div ref={placeholderRef} className="reasoning-placeholder">
{visible && (
<Suspense fallback={<div>Loading reasoning…</div>}>
<ReasoningChunk recommendationId={id} />
</Suspense>
)}
</div>
</div>
);
};
The ReasoningChunk component performs the fetch when it mounts. Good cdss keeps the rest of the workflow predictable.
// src/components/ReasoningChunk.tsx
import React, { useEffect, useState } from 'react';
export const ReasoningChunk: React.FC<{ recommendationId: string }> = ({ recommendationId }) => {
const [reasoning, setReasoning] = useState<string>('');
useEffect(() => {
fetch(`/api/recommendations/${recommendationId}`)
.then((res) => res.json())
.then((data) => setReasoning(data.text));
}, [recommendationId]);
return <pre>{reasoning}</pre>;
};
Resulting User Experience
-
Fast first paint – The recommendation text is rendered immediately; the reasoning placeholder is a lightweight
<div>. - On‑demand data – The network request for the reasoning is made only when the user scrolls close to the chunk, saving bandwidth.
- Consistent performance – Even on slow connections, the UI remains responsive because heavy text is loaded asynchronously.
Integration with the CDSS Workflow
When a clinician opens a patient case, the page loads a list of recommendations. Each recommendation is rendered with the component above. The lazy‑loading strategy keeps the initial payload small, allowing the clinician to focus on the most critical information without waiting for all AI explanations to download. When the clinician decides to explore a recommendation further, the reasoning appears seamlessly, preserving the flow of clinical decision support.
Conclusion
Documedic’s lazy‑loading of reasoning chunks is a concrete example of how careful architectural choices can improve the performance of a medical AI CDSS. By combining NestJS, React, PostgreSQL, and OpenRouter, the system delivers high‑quality AI explanations without compromising the speed clinicians expect from a clinical decision support tool.
Top comments (0)