Skip to content

Commit 68990e9

Browse files
committed
fix: update Vercel handler to use getRequestListener for improved request handling
1 parent 5381c71 commit 68990e9

1 file changed

Lines changed: 26 additions & 28 deletions

File tree

apps/studio/api/index.ts

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
*
99
* IMPORTANT: Vercel's Node.js runtime calls serverless functions with the
1010
* legacy `(IncomingMessage, ServerResponse)` signature — NOT the Web standard
11-
* `(Request) → Response` format. Using `handle()` from `hono/vercel` (Edge
12-
* adapter) would return a `Response` that nobody reads, while `res.end()` is
13-
* never called, causing a 300-second timeout.
11+
* `(Request) → Response` format.
1412
*
15-
* We use `handle()` from `@hono/node-server/vercel` instead, which properly
16-
* converts `IncomingMessage → Request`, calls `app.fetch()`, then writes the
17-
* `Response` back to `ServerResponse`.
13+
* We use `getRequestListener()` from `@hono/node-server` which properly
14+
* converts `IncomingMessage → Request`, calls our fetch callback, then writes
15+
* the `Response` back to `ServerResponse`. The fetch callback is called
16+
* directly (no outer Hono app relay) so POST body streams are consumed by
17+
* exactly one Hono instance — avoiding the body-lock hang that occurs when
18+
* an intermediate Hono app reads `c.req.raw` before forwarding.
1819
*
1920
* All kernel/service initialisation is co-located here so there are no
2021
* extensionless relative module imports — which would break Node's ESM
@@ -25,8 +26,8 @@ import { ObjectKernel, DriverPlugin, AppPlugin } from '@objectstack/runtime';
2526
import { ObjectQLPlugin } from '@objectstack/objectql';
2627
import { InMemoryDriver } from '@objectstack/driver-memory';
2728
import { createHonoApp } from '@objectstack/hono';
28-
import { Hono } from 'hono';
29-
import { handle } from '@hono/node-server/vercel';
29+
import { getRequestListener } from '@hono/node-server';
30+
import type { Hono } from 'hono';
3031
import { createBrokerShim } from '../src/lib/create-broker-shim.js';
3132
import studioConfig from '../objectstack.config.js';
3233

@@ -158,32 +159,29 @@ async function ensureApp(): Promise<Hono> {
158159
// Vercel handler
159160
// ---------------------------------------------------------------------------
160161

161-
const app = new Hono();
162-
163162
/**
164-
* Delegate every request to the lazily-initialized ObjectStack Hono app.
165-
* `ensureApp()` boots the kernel on the first invocation (cold start)
166-
* and returns the cached instance on subsequent warm invocations.
163+
* `getRequestListener` from `@hono/node-server` converts
164+
* `IncomingMessage → Request`, calls our fetch callback, then writes the
165+
* `Response` back to `ServerResponse` (including `res.end()`).
166+
*
167+
* By calling `ensureApp()` inside the fetch callback we get lazy kernel
168+
* boot AND the Request (with its body stream) is handed directly to the
169+
* ObjectStack Hono app — no intermediate Hono relay that would lock the
170+
* body stream before the real handler can read it.
167171
*/
168-
app.all('*', async (c) => {
172+
export default getRequestListener(async (request) => {
169173
try {
170-
const inner = await ensureApp();
171-
return await inner.fetch(c.req.raw);
174+
const app = await ensureApp();
175+
return await app.fetch(request);
172176
} catch (err: any) {
173177
console.error('[Vercel] Handler error:', err?.message || err);
174-
return c.json(
175-
{ success: false, error: { message: err?.message || 'Internal Server Error', code: 500 } },
176-
500,
178+
return new Response(
179+
JSON.stringify({
180+
success: false,
181+
error: { message: err?.message || 'Internal Server Error', code: 500 },
182+
}),
183+
{ status: 500, headers: { 'Content-Type': 'application/json' } },
177184
);
178185
}
179186
});
180187

181-
/**
182-
* `handle()` from `@hono/node-server/vercel` returns an
183-
* `async (IncomingMessage, ServerResponse) => void` handler that:
184-
* 1. Converts IncomingMessage → standard Request
185-
* 2. Calls app.fetch(request)
186-
* 3. Writes the Response back to ServerResponse (including res.end())
187-
*/
188-
export default handle(app);
189-

0 commit comments

Comments
 (0)