Skip to content

Stop asking the server for things nobody needs - #49

Merged
yt314 merged 1 commit into
mainfrom
fewer-pointless-requests
Sep 19, 2026
Merged

yt314 merged 1 commit into
mainfrom
fewer-pointless-requests

Conversation

@yt314

@yt314 yt314 commented Sep 18, 2026 •

Copy link
Copy Markdown
Owner

I counted the API calls the app makes on each screen load and per minute while idle. Two sources buy nothing.

1. The unread counters polled a tab nobody was looking at

The messages icon and the notifications bell each refresh once a minute. A plain timer keeps doing that in a background tab: a tab left open overnight sends hundreds of requests for a badge nobody can see, and on a phone it spends data and battery on the same.

Measured over 150 seconds with the tab reporting hidden:

before → polls while hidden: 4
after  → polls while hidden: 0

The timer now runs only while the tab is visible. It also fires the moment the tab comes back:

polls in the first 2.5s after coming back: 2  /api/messages/unread-count /api/notifications/unread-count

So the counter is actually fresher on return than the old timer left it — before, she could come back to a badge up to a minute stale.

2. GET /api/teachers/me three times per page load

### /app/dashboard — 13 API calls
    3× GET /api/teachers/me
### /app/lessons — 11 API calls
    3× GET /api/teachers/me

Three callers, same request, same answer, one page load:

caller what it wanted
profileSetupGuard → ProfileSetupService.load() isProfileComplete
TeacherShellComponent fullName for the header
DashboardComponent → load() again isProfileComplete, which the guard had just fetched

ProfileSetupService already documents the intent — "נשמר במטמון כאן ולא נשלף בכל ניווט" — but load() always hit the network, so the cache only ever helped the guard.

They now share one request, held in TeacherProfileService. Two things make it safe:

  • Keyed by the signed-in account, not cleared on logout. Whoever signs in next gets their own profile even if the sign-out path never ran — there is no logout hook left to remember to wire this into. Verified by signing out and signing in as a different teacher in the same tab: the header read רחל כהן, not מרים לוי.
  • Dropped whenever anything changes the profile (details, subjects, photo upload, photo delete), and dropped on a failed request so an error is never replayed to later callers as if it were the answer. Verified by editing a field, navigating away and back, and seeing the new value.
teachers/me requests on one dashboard load: 3 → 1

Result

/app/dashboard  13 → 11 API calls
/app/lessons    11 →  9
/app/students    6 →  5
/app/payments    7 →  6
/app/messages    5 →  4

Also checked — nothing to fix

  • No N+1 in the controllers: the list endpoints each run exactly 1 SQL query (/api/lessons for a month, /api/students, /api/payments, /api/notifications, a student card), measured against the EF command log.
  • The foreach loops flagged by a first scan in PaymentsController, StudentsController and TeachersController all await outside the loop — not N+1.

Testing

  • npx ng build — clean.
  • npx vitest run — 30 passing (app.spec.ts fails on main too, unrelated).
  • Every number above was measured in the browser, before and after.

Demo data untouched — the one field edited to test cache invalidation was restored to קטמון and verified in the database.

Two sources of requests that buy nothing.

1. The unread counters polled every 60 seconds whether or not anyone was
   looking. A tab left open overnight sent hundreds of requests for a badge
   nobody could see, and on a phone that is data and battery too. The timer
   now runs only while the tab is visible, and fires immediately when it comes
   back — so the counter is fresher on return than the plain timer left it,
   not staler. Measured over 150 seconds in the background: 4 requests before,
   0 after.

2. GET /api/teachers/me went out three times on a single page load — once
   from the setup guard, once from the shell for the name in the header, and
   once from the dashboard for a flag the guard had just fetched. They now
   share one request. The cache is keyed by the signed-in account, so the next
   person to sign in gets their own profile even if the sign-out never ran,
   and it is dropped whenever anything changes the profile.

Dashboard 13 → 11 API calls per load, lessons 11 → 9, messages 5 → 4.
@vercel

vercel Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
talmidon Ready Ready Preview Sep 18, 2026 8:58am UTC

@yt314
yt314 merged commit 87bc870 into main Sep 19, 2026
7 checks passed

This branch was successfully deployed

1 active deployment
Preview — 405357f8 Deployed Sep 18, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant