Skip to content

feat(api): add v2 /post/[author]/[permlink] read endpoint - #35

Open
rferrari wants to merge 5 commits into
SkateHive:mainfrom
rferrari:feat/v2-post-endpoint
Open

feat(api): add v2 /post/[author]/[permlink] read endpoint#35
rferrari wants to merge 5 commits into
SkateHive:mainfrom
rferrari:feat/v2-post-endpoint

Conversation

@rferrari

@rferrari rferrari commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

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_content RPC 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 /post endpoint 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

  • Queries HAFSQL with the same column list, children subquery and vote aggregation as fetchFeedData — so a single post is shape-compatible with feed items (reuses normalizePost).
  • Runs the result through dealiasSoftPosts (soft-post/lite-user parity, audit coupling point C2).
  • Serves s-maxage=300 for hits, short s-maxage=60 for 404, no cache on 500.
  • Extracts the cache-header trio into a small reusable cacheHeaders(sMaxage, swr) helper (used by the new route only; existing routes untouched).

HAFSQL over get_content deliberately: 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 /post as the first covered endpoint. Future endpoints can be built test-driven; existing endpoints get backfilled later.

  • Unit (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.
  • Smoke (tests/test-api-endpoints.sh): happy-path + 404 curl checks added.
  • Manual against real HAFSQL/Supabase: 200 shape field-identical to the same post from /api/v2/feed; 404 returns the short-cached error; soft-post output byte-identical to the feed's de-aliased output.
  • pnpm lint clean; pnpm build compiles the route as dynamic.

Commits

  1. refactor(api) cacheHeaders helper
  2. feat(api) /post route
  3. test(api) smoke checks
  4. chore(api) vitest runner
  5. test(api) /post unit tests

Notes

  • Additive only — no v3, no changes to existing routes, no cacheHeaders retrofit, no webapp changes.
  • One soft-post caveat: verified the de-alias code path is identical to the feed's, but a post with a resolvable overlay row wasn't available in sample data — worth a spot-check with known soft-post data before the webapp relies on it.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a new API endpoint to fetch a single post by author and permalink, with consistent response formatting and caching.
  • Bug Fixes

    • Returns clear 404 and 500 responses when a post is missing or a request fails.
    • Improves cache behavior for successful and missing post responses.
  • Tests

    • Added unit coverage for successful fetches, missing posts, error handling, and cache headers.
    • Expanded endpoint checks to include the new single-post route.
  • Chores

    • Added test scripts and configured the test runner.

rferrari and others added 5 commits July 3, 2026 00:56
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>
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This 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.

Changes

Single Post Endpoint

Layer / File(s) Summary
Cache headers helper
src/lib/cache-headers.ts
Adds cacheHeaders returning Cache-Control, CDN-Cache-Control, and Vercel-CDN-Cache-Control headers based on sMaxage/swr.
Post route query and handler
src/app/api/v2/post/[author]/[permlink]/route.ts
Defines POST_QUERY SQL and implements GET, returning cached 200/404/500 JSON responses after normalization and de-aliasing.
Vitest setup and unit tests
vitest.config.ts, package.json, tests/unit/post.route.test.ts
Configures Vitest (node env, @ alias, test include glob), adds test/test:watch scripts and vitest devDependency, and adds unit tests mocking DB and de-alias behavior for success, 404, and 500 cases.
Shell endpoint checks
tests/test-api-endpoints.sh
Adds a V2 POST ENDPOINT section testing valid and non-existent permlink lookups.

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
Loading

Related issues: None found.

Suggested labels: enhancement, tests

Suggested reviewers: None identified.

Poem
A rabbit hops through routes anew,
One post fetched, cached and true.
With tests that mock and vitest hum,
Four hundred four when posts are none.
Hooray, a squeak, the checks pass through! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding the v2 single-post read endpoint.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/app/api/v2/post/[author]/[permlink]/route.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

src/lib/cache-headers.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

tests/unit/post.route.test.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

  • 1 others

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
package.json (1)

48-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Vitest 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

📥 Commits

Reviewing files that changed from the base of the PR and between 3bb3c8d and 8450a5d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • package.json
  • src/app/api/v2/post/[author]/[permlink]/route.ts
  • src/lib/cache-headers.ts
  • tests/test-api-endpoints.sh
  • tests/unit/post.route.test.ts
  • vitest.config.ts

@rferrari

rferrari commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

Part of #38 (read-through-API tracking issue).

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