chore(api): standardize JSON field casing on snake_case + add CLAUDE.md - #188
Merged
Conversation
The DB is snake_case throughout and the model interfaces mirror it, but a
handful of API routes were translating to camelCase at the boundary. That
mapping was the source of "I renamed the column but a route still says
gridLocator" bugs. Drop the translation and have the API speak snake_case
end-to-end.
API surface changes (request + response field names):
- POST /api/auth/login response: gridLocator -> grid_locator
- POST /api/auth/register request + response: gridLocator -> grid_locator
- POST /api/install/create-admin request: gridLocator -> grid_locator
- GET /api/stats/advanced response:
countryDistribution -> country_distribution
continentDistribution -> continent_distribution
gridActivity -> grid_activity
gridSquare -> grid_square
- GET /api/contacts/callsigns response:
contactCount -> contact_count
lastContact -> last_contact
Frontend consumers updated in the same PR (register page, install page,
stats page, SearchInput). React form-state variables are intentionally
unchanged - they never cross the wire; the translation happens at fetch().
Also:
- Add CLAUDE.md documenting the snake_case convention, error response shape,
no-console policy, and code layout, so future work doesn't re-introduce
drift.
- Add `typecheck` script (tsc --noEmit) to package.json and fix the one
pre-existing TS error (tests/database-integration.spec.ts null guard) so
the script runs clean.
Known follow-up (not in this PR): GET /api/contacts/search still accepts
camelCase query params (gridLocator, startDate, endDate, qslStatus). Will
be addressed in a separate sweep.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
6 tasks
patrickrb
added a commit
that referenced
this pull request
May 11, 2026
…nt rule (#189) The codebase had ~109 console.log/info/debug calls scattered through src/, mostly QRZ/LoTW sync debug scaffolding and ADIF import batch tracers. None of them shipped useful runtime info — they were development breadcrumbs that never got cleaned up. Strip them and add an ESLint rule so they can't quietly come back. Heavy hitters cleaned: - src/app/api/contacts/qrz-sync/route.ts: 28 per-contact debug traces - src/lib/qrz.ts: 20 HTTP request/response traces - src/app/api/contacts/qrz-download/route.ts: 8 - src/app/api/install/finalize/route.ts: 7 (one converted to console.warn) - src/app/api/cron/lotw-{download,upload}/route.ts: 12 combined (auth/header debug + per-station progress) - src/app/api/adif/import/route.ts + src/components/AutoImportADIF.tsx: 13 batch-progress traces - src/app/api/lotw/download{,-contact}/route.ts: 12 credential-path traces - src/app/api/install/migrate-schema/route.ts: 4 - Smaller cleanups in UserContext, search/page, dxpeditions, settings Kept: - console.error in genuine error paths (unchanged policy) - console.warn for real warnings (e.g. fallback paths in Propagation, install/database, install/finalize, install/migrate-schema) - The two intentional console.log calls in src/lib/logger.ts (with eslint-disable-next-line annotations, since the logger module is precisely what the rest of the codebase should use going forward) ESLint: - Added `no-console` rule with `allow: ["warn", "error"]` at error level - Exempted scripts/**/*.{js,mjs} (CLI utilities legitimately use console) - Exempted tests/**/*.{ts,js} (diagnostic output in test failures) Also picks up the same one-line null-guard fix from PR #188 in tests/database-integration.spec.ts so this branch's `tsc --noEmit` runs clean independent of merge order. Lint: 0 errors, 52 warnings (unchanged baseline; React hooks warnings tracked separately for PR 3). Build: succeeds. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
patrickrb
added a commit
that referenced
this pull request
May 11, 2026
Starting from 52 warnings. End state: 0 errors, 0 warnings. Mix of substantive fixes and one documented rule downgrade. Each category: Hoisting / stale-closure (17 warnings) — wrapped fetcher functions in useCallback and moved their declarations above the useEffects that call them. Adds proper dep arrays. Affected pages: - adif/page.tsx (fetchStations) - admin/storage/page.tsx (fetchConfigs) - admin/users/page.tsx (fetchUsers) - awards/dxcc/page.tsx (fetchDXCCSummary) - awards/was/page.tsx (loadStations) - new-contact/page.tsx (fetchStations, fetchCurrentUser) - stations/[id]/edit/page.tsx (fetchStation + 3 siblings) - stations/new/page.tsx (fetchDxccEntities, fetchStatesProvinces) - stations/page.tsx (fetchStations, fetchStationStats) - stats/page.tsx (fetchStations) - search/page.tsx (performSearch, debouncedSearch — moved above the useEffect that triggers it) Memoization warning (1) — search/page.tsx `debouncedSearch` was caught by react-hooks/preserve-manual-memoization because it depended on `searchTimeout` state and called `setSearchTimeout`, recreating itself on every tick. Replaced the state with a useRef so the callback's identity is stable. This is the React-19-compiler-recommended pattern for timer state. set-state-in-effect (33 warnings) — disabled. These all fired on the standard "fetch data on mount → setState with the result" pattern, which is normal React data-loading. Per-line suppression would add 33 comments across the codebase, noisier than the warning itself. eslint.config.mjs carries a comment explaining the decision and pointing at the path to re-enable (adopt SWR/TanStack Query, which obviates the pattern). Unused eslint-disable in storage.ts (1) — the comments suppressed no-unused-vars for parameters that already had `_` prefix. Added `argsIgnorePattern: "^_"` to the project ESLint config (standard JS/TS convention for "intentionally unused"), then dropped the now-redundant disable comments. Two follow-on `_mimeType` warnings disappeared too. Also picks up the same one-line null-guard in tests/database-integration .spec.ts as PRs #188/#189, so this branch's `tsc --noEmit` is clean independent of merge order. Verification: - npm run lint → 0 errors, 0 warnings - npx tsc --noEmit → clean - npm run build → succeeds Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Eliminates a class of "column-renamed-but-the-route-still-says-camelCase" bugs by unifying the API surface with the DB schema. The DB is already
snake_casethroughout and model interfaces (e.g.ContactData,User) mirror it — only a handful of routes were translating to camelCase at the boundary. This drops the translation.API surface changes (request + response field names):
POST /api/auth/loginresponse:gridLocator→grid_locatorPOST /api/auth/registerrequest + response:gridLocator→grid_locatorPOST /api/install/create-adminrequest:gridLocator→grid_locatorGET /api/stats/advancedresponse:countryDistribution/continentDistribution/gridActivity/gridSquareall → snake_caseGET /api/contacts/callsignsresponse:contactCount/lastContact→ snake_caseFrontend consumers updated in the same PR: register page, install page, stats page,
SearchInput. React form-state variable names are intentionally left as camelCase — they're internal component state, never crossing the wire (the translation happens at thefetch()call site).Also:
CLAUDE.mddocuments the convention (snake_case end-to-end, error shape{ error: string }, no-console policy, code layout) so future work doesn't re-introduce drift.UserContext.tsxalready expected snake_case — the API just wasn't delivering it.typecheckscript (tsc --noEmit) inpackage.json. Fixed the one pre-existing TS error (tests/database-integration.spec.ts:40null guard) so the script runs clean from day one.Known follow-up (not in this PR)
GET /api/contacts/searchstill accepts camelCase query params (gridLocator,startDate,endDate,qslStatus). Bigger touch (multiple keys + the search/filter page), splitting into its own PR.src/lib/lotw.tsand the awards routes use camelCase — fine, they're not API surface.Test plan
npm run typecheckcleannpm run lint— 52 warnings (unchanged from main; tracked in plan as PR 3)npm run buildsucceeds/stats, confirm geographic distribution panels render🤖 Generated with Claude Code