feat(api): add v2 /post/[author]/[permlink] read endpoint - #35
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fetches a single post from HAFSQL with the same column shape, children count and vote aggregation as the feed, then runs it through dealiasSoftPosts so lite-user (@skateuser) identity is preserved. Serves edge-cached (s-maxage=300) so post pages, OG images and Farcaster frames stop hitting Hive RPC directly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers the happy path (single post by author/permlink) and the 404 (nonexistent permlink) in the existing status-code smoke suite. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Introduces vitest (+ test/test:watch scripts and a config that mirrors the '@/*' tsconfig alias) as the unit-test framework for API routes. Unit tests live in tests/unit/ so route folders with literal-bracket segments (e.g. [author]) don't break glob includes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers 200 (normalized shape + 300s edge-cache headers), query parameterization, soft-post de-alias delegation, 404 (short cache), and 500 (no cache). HAFSQL and dealiasSoftPosts are mocked so the suite runs with no live DB. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR adds a new GET API route for fetching a single post by author/permlink, backed by a SQL query, normalization, and soft-post de-aliasing. It introduces a shared cache-headers helper, Vitest test configuration and scripts, unit tests for the route, and a shell script check. ChangesSingle Post Endpoint
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant GETHandler as GET Handler
participant hafDb as hafDb.executeQuery
participant Dealias as dealiasSoftPosts
Client->>GETHandler: GET /api/v2/post/[author]/[permlink]
GETHandler->>hafDb: executeQuery(POST_QUERY, author, permlink)
hafDb-->>GETHandler: rows
alt no rows
GETHandler-->>Client: 404 Post not found (short cache)
else row found
GETHandler->>Dealias: dealiasSoftPosts(normalizedPost)
Dealias-->>GETHandler: post
GETHandler-->>Client: 200 data (long cache)
end
Related issues: None found. Suggested labels: enhancement, tests Suggested reviewers: None identified. Poem 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
src/app/api/v2/post/[author]/[permlink]/route.tsESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox. src/lib/cache-headers.tsESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox. tests/unit/post.route.test.tsESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
package.json (1)
48-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueVitest is pinned well behind current major.
Vitest 2.1.9 is two majors behind the current release line (4.x as of early 2026); worth a follow-up bump once convenient, though not blocking for this PR.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@package.json` around lines 48 - 49, Vitest is pinned to an older major version in the package manifest, so update the vitest dependency in the package.json dependencies/devDependencies entry to the current major release line and verify any related test setup or API usage still matches the new version; use the existing vitest package entry as the anchor for the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@package.json`:
- Around line 48-49: Vitest is pinned to an older major version in the package
manifest, so update the vitest dependency in the package.json
dependencies/devDependencies entry to the current major release line and verify
any related test setup or API usage still matches the new version; use the
existing vitest package entry as the anchor for the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0d050ea4-f6dc-4c46-94e5-3320e603b447
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
package.jsonsrc/app/api/v2/post/[author]/[permlink]/route.tssrc/lib/cache-headers.tstests/test-api-endpoints.shtests/unit/post.route.test.tsvitest.config.ts
|
Part of #38 (read-through-API tracking issue). |
What
Adds a single-post v2 read endpoint:
GET /api/v2/post/[author]/[permlink].This is the first of a few small PRs to close the API read gaps identified in the webapp "read-through-API" migration — moving individual-post fetches off direct Hive
get_contentRPC and onto edge-cached v2.Why
Today the webapp fetches individual posts via direct RPC (server-rendered post pages, OG-image routes, Farcaster frame embeds) — none of it edge-cacheable. A v2
/postendpoint makes those paths edge-cached and applies the same server-side soft-post de-aliasing the feed already does, so lite-user (@skateuser) identity is preserved.How
childrensubquery and vote aggregation asfetchFeedData— so a single post is shape-compatible with feed items (reusesnormalizePost).dealiasSoftPosts(soft-post/lite-user parity, audit coupling point C2).s-maxage=300for hits, shorts-maxage=60for 404, no cache on 500.cacheHeaders(sMaxage, swr)helper (used by the new route only; existing routes untouched).HAFSQL over
get_contentdeliberately: gives soft-post de-alias parity and identical field shape to the feed the webapp already consumes, and is edge-cacheable off Postgres rather than public RPC.Testing
Introduces vitest as the API unit-test framework (
pnpm test), with/postas the first covered endpoint. Future endpoints can be built test-driven; existing endpoints get backfilled later.tests/unit/post.route.test.ts, 5 tests, no live DB — HAFSQL + dealias mocked): 200 shape + cache headers, query parameterization, soft-post delegation, 404 short-cache, 500 no-cache.tests/test-api-endpoints.sh): happy-path + 404 curl checks added./api/v2/feed; 404 returns the short-cached error; soft-post output byte-identical to the feed's de-aliased output.pnpm lintclean;pnpm buildcompiles the route as dynamic.Commits
refactor(api)cacheHeaders helperfeat(api)/post routetest(api)smoke checkschore(api)vitest runnertest(api)/post unit testsNotes
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests
Chores