Skip to content

fix(studio): replace custom getRequestListener with handle() from @hono/node-server/vercel - #1001

Merged
hotlong merged 2 commits into
mainfrom
copilot/fix-api-endpoint-responses
Mar 31, 2026
Merged

fix(studio): replace custom getRequestListener with handle() from @hono/node-server/vercel#1001
hotlong merged 2 commits into
mainfrom
copilot/fix-api-endpoint-responses

Conversation

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

/api/v1/packages and /api/v1/meta return the SPA's index.html instead of JSON on Vercel because the serverless function export was using a hand-rolled getRequestListener() callback — diverging from the official Vercel adapter pattern and causing Vercel's function resolver to fail silently, falling through to the SPA rewrite.

Changes

  • apps/studio/server/index.ts
    • Swap getRequestListener (manual) → handle() from @hono/node-server/vercel (official adapter)
    • Introduce an outer Hono app using the documented outer→inner delegation pattern; handle(app) becomes the default export
    • Drop the custom extractBody helper — @hono/node-server/vercel already handles Vercel's pre-buffered rawBody natively
// Before — custom getRequestListener callback, fragile body extractionexportdefaultgetRequestListener(async(request,env)=>{constincoming=(envasany)?.incoming;constbody=extractBody(incoming,method,contentType);// manualreturnapp.fetch(newRequest(url,{ method, headers, body }));});// After — standard handle() + outer→inner delegationconstapp=newHono();app.all('*',async(c)=>{constinner=awaitensureApp();returninner.fetch(c.req.raw);});exportdefaulthandle(app);// @hono/node-server/vercel
  • .changeset/fix-vercel-api-html-response.md — patch changeset for @objectstack/studio
Original prompt

Problem

After deploying to Vercel, the API endpoints incorrectly return HTML (the SPA's index.html) instead of JSON responses:

Root Cause Analysis

The deployment architecture has a critical mismatch between where the serverless function is built and where Vercel expects to find it:

  1. vercel.json sets "outputDirectory": "dist" (Vite build output for the SPA)
  2. bundle-api.mjs bundles server/index.tsapi/index.js (in the apps/studio/ root, NOT in dist/)
  3. The rewrite rule { "source": "/api/(.*)", "destination": "/api" } expects a serverless function at api/index.js, but Vercel looks for functions relative to the project root OR the output directory
  4. When Vercel can't properly resolve the serverless function, the request falls through to the SPA fallback rewrite { "source": "/((?!api/).*)", "destination": "/index.html" }, which returns HTML

Additionally, the current server/index.ts exports via getRequestListener() from @hono/node-server, which returns a (IncomingMessage, ServerResponse) => void Node listener. While this can work, Vercel's Node.js runtime has better compatibility with the @hono/vercel adapter's handle() function which properly wraps the app for Vercel's expected function signature.

Required Fix (Sustainable, Production-Grade)

1. Fix apps/studio/scripts/bundle-api.mjs

The bundled output api/index.js must be placed where Vercel can find it as a serverless function. Since outputDirectory is dist, and Vercel discovers functions from the project directory (not the output directory), the current api/index.js path should work IF the function is correctly exported.

2. Fix apps/studio/server/index.ts export

Replace getRequestListener() with the standard Vercel adapter pattern using handle() from @hono/vercel:

import{handle}from'@hono/node-server/vercel';import{Hono}from'hono';// ... kernel bootstrap code stays the same ...constapp=newHono();app.all('*',async(c)=>{constinner=awaitensureApp();returninner.fetch(c.req.raw);});exportdefaulthandle(app);

Or alternatively, keep the current approach but ensure the export is compatible:

// The handler must be a default export that Vercel recognizesimporttype{IncomingMessage,ServerResponse}from'http';consthandler=async(req: IncomingMessage,res: ServerResponse)=>{// ... handle request};exportdefaulthandler;

3. Fix apps/studio/vercel.json rewrites

The rewrites must correctly handle the ordering. The current /api/(.*)/api rewrite should work, but ensure the SPA fallback doesn't accidentally catch API routes. Consider using headers to ensure API responses have the correct Content-Type.

4. Ensure api/index.js is available to Vercel

The bundle-api.mjs script runs as part of the build command. Verify that Vercel can access the output at api/index.js after the build completes. The .gitignore already ignores api/index.js and api/index.js.map, confirming they are build artifacts.

Key Files to Examine and Modify

  • apps/studio/vercel.json — Rewrites and function configuration
  • apps/studio/server/index.ts — Serverless function entrypoint
  • apps/studio/scripts/bundle-api.mjs — Build script that bundles the API function
  • apps/studio/package.json — Dependencies (ensure @hono/node-server or hono/vercel is available)

Verification

After the fix:

  1. Run existing tests: pnpm turbo run test --filter=@objectstack/studio
  2. Run the Hono adapter tests: pnpm turbo run test --filter=@objectstack/hono
  3. Verify that GET /api/v1/meta returns { "success": true, ... } JSON
  4. Verify that GET /api/v1/packages returns { "success": true, ... } JSON
  5. Verify that non-API routes still serve the SPA index.html

Context from CHANGELOG

From apps/studio/CHANGELOG.md, version 3.2.9 introduced:

Migrate Vercel API entrypoint from api/[...path].ts to api/index.ts (Hono + Vercel Node adapter)

  • Add /api/*/api rewrite in vercel.json for native Hono routing

And version 3.2.10:

Fix Vercel deployment crash (ERR_MODULE_NOT_FOUND for api/_kernel)

  • Inline _kernel.ts content into api/index.ts

The current server/index.ts + bundle-api.mjs approach was the latest evolution, but the Vercel function resolution is broken.

Important Notes

  • This is a metadata platform project — use a sustainable, well-architected solution, not a temporary workaround
  • After fixing, update CHANGELOG.md to reflect the fix
  • Run tests to ensure nothing is broken

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: 部署到 vercel 后, api 错误的返回了html https://play.objectstack.ai/api/v1/packageshttps://play.objectstack.ai/api/v1/meta

Assistant:

Let me start by understanding the repository s...

This pull request was created from Copilot chat.

@vercel

vercelBot commented Mar 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
objectstack-playReadyReadyPreview, CommentMar 31, 2026 3:16am
specReadyReadyPreview, CommentMar 31, 2026 3:16am

Request Review

CopilotAI changed the title [WIP] Fix API endpoints returning HTML instead of JSONfix(studio): replace custom getRequestListener with handle() from @hono/node-server/vercelMar 31, 2026
CopilotAI requested a review from hotlongMarch 31, 2026 03:12
@hotlong
hotlong marked this pull request as ready for review March 31, 2026 03:17
CopilotAI review requested due to automatic review settings March 31, 2026 03:17
@hotlong
hotlong merged commit 999ea4f into mainMar 31, 2026
3 checks passed

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes Vercel deployments where /api/v1/* routes were falling through to the SPA rewrite and returning index.html instead of JSON, by switching the serverless entrypoint export to Hono’s official Vercel adapter.

Changes:

  • Replace the custom getRequestListener-based export with handle() from @hono/node-server/vercel.
  • Introduce an outer Hono app that delegates all requests to the lazily-booted inner ObjectStack Hono app via inner.fetch(c.req.raw).
  • Add a patch changeset for @objectstack/studio documenting the Vercel fix.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

FileDescription
apps/studio/server/index.tsSwitches to the official Hono Vercel adapter (handle) and uses an outer→inner delegation wrapper to ensure Vercel recognizes the function handler.
.changeset/fix-vercel-api-html-response.mdAdds a patch changeset describing the production fix and why the adapter change resolves the HTML response issue.

Sign up for freeto 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.

3 participants

@hotlong