Skip to content

Developer view · drbharada-website

Exit developer view
Files

content/posts/fewer-api-calls-angular-dashboards.mdx

Open as page
---title: "Why your Angular dashboard makes too many API calls"slug: "fewer-api-calls-angular-dashboards"date: "2026-10-04"description: "Four patterns I look for when a data-heavy Angular screen feels slow, and how to measure the difference before you change anything."cover: charttags:  - Angular  - Performance  - RxJS--- Most slow dashboards I have worked on were not slow because of the grid, the framework, or the server. They were slow because the frontend asked for the same data again and again, often several times a second. On one enterprise Angular app, caching and tighter state management cut our API calls by 40%. The backend did not change at all. <Stat value="40%" label="fewer API calls" detail="On a live enterprise Angular app, from caching and better state management. No backend changes." /> Here are the four patterns I now check first on any data-heavy screen. ## Start by counting Before changing anything, find out what the screen actually sends. The Network tab works, but a tiny interceptor gives you a running count per endpoint in the console: ```tsexport const requestCounter: HttpInterceptorFn = (req, next) => {  console.count(`${req.method} ${req.url}`);  return next(req);};``` Register it with `provideHttpClient(withInterceptors([requestCounter]))`, click through the screen like a real user, and read the numbers. Any endpoint with a count higher than you expected is where to start. ## 1. Filters that fire on every keystroke A search box wired straight to the API sends one request per letter. Two RxJS operators fix most of it: `debounceTime` waits until the user pauses, and `distinctUntilChanged` skips a search that matches the previous one. ```tsreadonly query = signal(''); readonly results = toSignal(  toObservable(this.query).pipe(    debounceTime(300),    distinctUntilChanged(),    switchMap((q) => this.api.search(q)),  ),  { initialValue: [] },);``` `switchMap` also cancels a request that is still in flight when a newer search starts, so an old, slow response can never overwrite a newer one. <BarChart  title="Requests sent while typing “invoice” into a filter"  caption="Worked example, not a measurement. The user pauses after “inv” and again after “invoice”, then quickly deletes the last letter and types it again."  valueLabel="Requests">  <Bar label="Every keystroke" value="9" />  <Bar label="debounceTime(300)" value="3" />  <Bar label="+ distinctUntilChanged" value="2" highlight="true" /></BarChart> ## 2. The same data, requested twice at once Status lists, user lookups and dropdown options are often needed by several components on the same screen. If each one calls the service, each one sends its own request. Share one request instead: ```tsprivate readonly statuses$ = this.http  .get<Status[]>('/api/statuses')  .pipe(shareReplay({ bufferSize: 1, refCount: false })); getStatuses() {  return this.statuses$;}``` Every subscriber now gets the same response, and only the first one triggers the call. This works well for reference data that rarely changes during a session. > If two components need the same data, the second one should never have to ask the server for it. ## 3. Navigation that refetches unchanged data Going from a list to a record and back usually reloads the whole list. For data that changes slowly, keep it in a store and only reload it when it is old or when you know it has changed. ```tsprivate readonly maxAgeMs = 60_000;private loadedAt = 0; loadCases() {  if (Date.now() - this.loadedAt < this.maxAgeMs) return;  this.loadedAt = Date.now();  this.api.getCases().subscribe((cases) => this.cases.set(cases));} invalidate() {  this.loadedAt = 0;}``` Call `invalidate()` after a save, so the next visit loads fresh data. Users get instant navigation without ever seeing stale records they just edited. ## 4. Grids that ask for more than they show AG Grid's server-side row model loads rows in blocks. The default block size is 100 rows, so a grid showing 25 rows per page can fetch four times more data than anyone sees. Two settings help: - Set `cacheBlockSize` to match your page size, so each request returns exactly one page.- Set `blockLoadDebounceMillis` to a small value, so fast scrolling does not fire a request for every block the user scrolls past. --- ## What to take away None of this is clever. It is the boring part of frontend work: count the requests, find the repeats, and remove the ones nobody needed. If your dashboard feels slow, add the counter first. The numbers usually point straight at the problem, and the fix is often a few lines.
⎇ developMDX119 linesRepo fileSite updated 18 Oct 2026