refactor(types): album/image snake_case fields → camelCase#466
Open
Zheaoli wants to merge 2 commits into
Open
Conversation
Renames four canonical fields end-to-end across the API surface and
frontend consumers, completing item 1 of the API refactor plan's
snake_case data-model leak for the Album/Image types:
- album_value → albumValue
- image_name → imageName
- image_sorting → imageSorting
- show_on_mainpage → showOnMainpage
The database schema is intentionally untouched. Prisma still maps the
underlying snake_case columns; the rename happens at the server
boundary so frontends consume camelCase without knowing the on-disk
naming.
Server boundary
---------------
- `server/lib/model-transform.ts` (new) exposes `toAlbum` / `toAlbumList`
for Prisma `findMany`/`findFirst` results, `mapRawImageRow(s)` for
raw-SQL `SELECT image.*` rows, and `toAlbumPrismaCreate` /
`toAlbumPrismaUpdate` / `toImagePrismaCreate` / `toImagePrismaUpdate`
for write paths.
- Raw SQL queries in `server/db/query/{images,daily}.ts` add `AS
"albumValue"` / `AS "showOnMainpage"` aliases where columns are
selected explicitly, and route `SELECT image.*` results through the
raw-row mapper. `server/db/query/dashboard.ts` already aliased; left
intact.
- `server/db/query/albums.ts` flips `fetchAlbumByRouter` to
`Promise<AlbumType | null>` to reflect the actual return type
(previously unsound).
- `server/db/operate/{albums,images}.ts` consume the camelCase input
shape and delegate snake_case mapping to the new helpers.
- `server/tasks/service.ts` aliases `image.image_name AS "imageName"` to
match the renamed `MetadataRefreshImage.imageName` field.
Hono handlers
-------------
- `hono/albums.ts` validates `album.albumValue.charAt(0)`.
- `hono/open/download.ts` reads `imageData.imageName` when deriving the
download filename.
Backup format decision
----------------------
The on-disk backup envelope (`types/backup.ts`, `server/backup/*`) is a
versioned serialization format, not an API surface. It mirrors Prisma's
snake_case column names by design. Keeping the backup format snake_case
means existing backups produced before this PR import cleanly without a
version bump or migration path. The repository's `mapAlbumRecord` /
`mapImageRecord` helpers continue to read Prisma rows and emit
snake_case backup records — no boundary mapping needed. See item 1 of
the "Known Deviations" snapshot in `CLAUDE.md` for the remaining
snake_case fields that are out of scope for this PR.
Frontend
--------
Component-level property access is renamed throughout
`components/admin/album/*`, `components/admin/list/*`,
`components/admin/upload/*`, `components/admin/tasks/tasks-page.tsx`,
`components/album/*`, `components/layout/top-nav.tsx`, and the
`tasks-page.tsx` `AlbumOption = Pick<AlbumType, 'id' | 'name' |
'albumValue'>` alias. RSS feed (`app/rss.xml/route.ts`) and the daily
settings page (`app/admin/settings/daily/page.tsx`) follow suit.
Scope discipline
----------------
Other snake_case fields surfaced through the public types — `preview_url`,
`video_url`, `album_name`, `album_license`, `random_show`,
`exposure_time`, `f_number`, `daily_weight`, and the `Config` row shape
consumed by storage client builders — are intentionally not touched.
They are tracked separately and will be addressed in follow-up PRs.
See docs/plans/2026-05-19-api-refactor-design.md PR-09.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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
PR-09 of the API refactor plan (
docs/plans/2026-05-19-api-refactor-design.md). The largest PR of the series — renames four snake_case fields end-to-end across the API surface and frontend consumers:album_value→albumValueimage_name→imageNameimage_sorting→imageSortingshow_on_mainpage→showOnMainpageThe Prisma schema and DB columns stay snake_case; the rename happens at the server boundary. Frontends, the wire format, and
types/index.tsnow use camelCase exclusively for these four fields.Server boundary
A new
server/lib/model-transform.tsprovides:toAlbum(row)/toAlbumList(rows)— for PrismafindMany/findFirstresults inserver/db/query/albums.ts.mapRawImageRow(row)/mapRawImageRows(rows)— for raw-SQLSELECT image.*results inserver/db/query/{images,daily}.ts. Renames the four canonical fields without touching the rest of the payload.toAlbumPrismaCreate/toAlbumPrismaUpdate— used byserver/db/operate/albums.tsto consumeAlbumType(camelCase) and emit Prisma's snake_case create/update input shape.toImagePrismaCreate/toImagePrismaUpdate— same pattern forserver/db/operate/images.ts.SQL aliases were added in the following raw queries (PostgreSQL preserves case only inside double-quoted identifiers):
server/db/query/images.ts—albums.id AS \"albumValue\"in five queries,image.show_on_mainpage AS \"showOnMainpage\"infetchMapImages,\"albums\".album_value AS \"albumValue\"infetchImageByIdAndAuth,A.album_value AS \"albumValue\"ingetRSSImages.server/db/query/daily.ts—albums.album_value AS \"albumValue\"infetchAlbumsWithDailyWeight.server/tasks/service.ts—image.image_name AS \"imageName\"infetchImagesBatchForScope.server/db/query/dashboard.ts— already aliased before this PR; untouched.server/db/query/albums.ts::fetchAlbumByRouterwas also tightened toPromise<AlbumType | null>(it could returnnullbut the type previously hid that).app/(theme)/[...album]/page.tsxwas updated to accept the nullable.Files (server)
types/index.tsserver/lib/model-transform.ts(new)server/db/query/{albums,images,daily}.tsserver/db/operate/{albums,images}.tsserver/tasks/{service,metadata-refresh}.tshono/albums.tshono/open/download.tsFiles (frontend)
app/(theme)/[...album]/page.tsxapp/admin/settings/daily/page.tsxapp/rss.xml/route.tscomponents/admin/album/{album-add-sheet,album-edit-sheet,album-list}.tsxcomponents/admin/list/{image-edit-sheet,image-view,list-props}.tsxcomponents/admin/tasks/tasks-page.tsxcomponents/admin/upload/{simple,livephoto,multiple}-file-upload.tsxcomponents/album/{preview-image,preview-image-exif}.tsxcomponents/layout/top-nav.tsxDocs
CLAUDE.md— "Known Deviations" item 1 updated: the four PR-09 fields plusExif.data_time(PR-08) and theConfigsettings shape (PR-07) are now marked done; remaining snake_case fields (e.g.preview_url,video_url,album_name,album_license,random_show,exposure_time,daily_weight) and the backup format are explicitly noted as out of scope.Backup-format decision
The on-disk backup envelope (
types/backup.ts,server/backup/format-adapter.ts,server/backup/prisma-repository.ts) keeps the existing snake_case field names.Rationale:
format: 'picimpact-backup',version: 1), not an API surface. Anyone consuming it does so explicitly via the import/export flow.mapAlbumRecord/mapImageRecordmappers — they consume Prisma rows directly and emit backup records without any extra translation.parseBackupEnvelope, both adding risk and surface area for a refactor PR.The backup repository continues to read/write snake_case end-to-end. The API boundary (Hono handlers, server actions, frontends) sees camelCase. The two domains never overlap.
Test plan
pnpm run lint— 0 errors (warnings unchanged from main).pnpm exec tsc --noEmit— 64 errors (2 fewer than the baseline-with-.next-types count of 66; the two removed errors were a pre-existing unsoundness infetchAlbumByRouterand a generatedmodel-transformconstraint that was incidentally fixed).pnpm run build— succeeds end-to-end; sitemap / RSS / route validators clean.fetchClientImagesListByAlbumand the random-show shuffle works./rss.xml— confirmsitem.albumValuereaches the feed item URL builder.imageNamein the POST body reaches Prisma asimage_name.toAlbumPrismaUpdatewrites correctly and the relation table'salbum_valueis updated when the route changes.showOnMainpageround-trips.fetchAlbumsWithDailyWeightreturns camelCasealbumValueto the settings page.parseBackupEnvelope.🤖 Generated with Claude Code