feat(vps): port favorites to HttpApiBuilder.group (Step 6)#166
Merged
guidefari merged 3 commits intoJul 11, 2026
Conversation
Owner
Author
|
Fixed: restored UUID format validation on audioId/showId (add payload + both delete params) using the same Schema.String.pipe(Schema.check(Schema.isPattern(...))) pattern as newsletter's fix in #165. Typecheck clean, same pre-existing DB-connectivity failure count. |
Ports POST /api/favorites, DELETE /api/favorites/:audioId,
DELETE /api/favorites/show/:showId, GET /api/favorites from the Hono
fallback onto the Effect router. All four are gated by
.middleware(AuthMiddleware).
Handlers go through the existing FavoriteService
(apps/vps/src/services/favorite.service.ts), which already returns typed
tagged errors (NotFoundError, ConflictError, DatabaseError) -- the
cleanest port so far, matching the search/profile/resolve shape rather
than admin/newsletter's raw-Drizzle-in-the-handler style.
The old zod schema's cross-field .refine() ("either audioId or showId
must be provided") has no direct Effect Schema equivalent used elsewhere
in this migration; kept both fields optional in the payload schema and
moved the check into the handler (400 if neither is present), rather
than inventing a new schema pattern for one endpoint.
Schema written against FavoriteService's real return type, which
includes a show field that apps/www/src/lib/http.ts's own Favorite type
currently omits -- a frontend-side gap, not corrected here.
Uses the shared dieOnDatabaseError helper.
Deletes the superseded routes/favorites/{favorites.routes,favorites.index,favorites.handlers}.ts
Hono trio and its app.ts mount.
Adversarial review found the same class of regression as newsletter's #165: the old zod schema validated audioId/showId as z.string().uuid() (on the add payload and both delete params), and the new Schema.String dropped that constraint entirely. Added a shared Uuid schema (Schema.String.pipe(Schema.check(Schema.isPattern(...)))) and applied it everywhere audioId/showId appear in packages/api/src/favorites.ts.
d3cdad9 to
1f05dda
Compare
guidefari
added a commit
that referenced
this pull request
Jul 11, 2026
Adversarial review found a third occurrence of the input-validation regression (after newsletter #165, favorites #166): the old zod schema used z.string().min(1) for bucketName/key/sourceBucket/destinationBucket, and the new Schema.String dropped that constraint. Switched to Schema.NonEmptyString (already used in search.ts's q field) for all four. Lower severity than the prior two cases -- an empty string here reaches S3Service/AWS SDK calls and surfaces as a 500 via the caught S3Error path, not a DB write or enumeration signal -- but fixed anyway per the now-mandatory checklist item in docs/migration-effect-http-api-process.md.
This was referenced Jul 11, 2026
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.
Why
Step 6 continues: port
favorites(POST /api/favorites,DELETE /api/favorites/:audioId,DELETE /api/favorites/show/:showId,GET /api/favorites) from the Hono fallback onto the Effect router.What to look for
FavoriteService(
apps/vps/src/services/favorite.service.ts), which already returnstyped tagged errors (
NotFoundError,ConflictError,DatabaseError) --this is the cleanest port so far, matching the search/profile/resolve
shape rather than admin/newsletter's raw-Drizzle-in-the-handler style.
.refine()("eitheraudioIdorshowIdmust be provided") has no direct Effect Schema equivalent usedelsewhere in this migration. Kept both fields optional in the payload
schema (
packages/api/src/favorites.ts'sAddFavoriteInput) and movedthe check into the handler (400 if neither is present), rather than
inventing a new cross-field schema pattern for one endpoint.
FavoriteWithContent's schema is written againstFavoriteService'sreal return type, which includes a
showfield --apps/www/src/lib/http.ts's ownFavoritetype currently omitsshowentirely. Frontend-side gap, not corrected in this PR (backend now
correctly serves the field either way).
dieOnDatabaseErrorhelper.Test evidence
All four endpoints have real
apps/wwwconsumers(
FavoritesSection.tsx,FavoriteButton.tsx,MixMenu.tsx,TrackContextMenu.tsx), so this isn't screenshot-exempt.Verified via an authenticated in-page
fetch(not a screenshot, since thetest account has zero favorites and an empty-list screenshot can't
distinguish "endpoint works, no data" from "endpoint broken") against the
real dev server:
Response shape matches the new
GetFavoritesResponseschema exactly.Given an earlier live-send mistake this session (an invite email
misfired to the wrong user on PR #164), no favorite/unfavorite mutation
was triggered live -- auth-gating on all four endpoints is covered by 4
new blackbox tests instead.
bun run typecheck(vps and api packages): clean.vitest run --config vitest.unit.config.ts(vps): added 4 new blackboxcases (401 for all four endpoints without a session cookie), all pass.
Pre-existing DB-connectivity failures (8, same as
migration/effect-http-api's current tip) are unchanged by this PR.