Skip to content

Commit 7dc86cc

Browse files
Merge pull request #1147 from objectstack-ai/claude/deploy-server-to-vercel-again
Configure dual-driver setup for Vercel deployment: Turso for sys namespace, Memory for apps
2 parents 72d34af + cce964d commit 7dc86cc

File tree

7 files changed

+62
-7
lines changed

7 files changed

+62
-7
lines changed

apps/server/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# Build artifacts
22
dist/
33
.turbo/
4+
public/
45

56
# Bundled API handler (generated during Vercel build)
67
api/_handler.js
78
api/_handler.js.map
9+
api/node_modules/
810

911
# Node modules
1012
node_modules/

apps/server/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
"@objectstack/service-automation": "workspace:*",
3636
"@objectstack/service-feed": "workspace:*",
3737
"@objectstack/spec": "workspace:*",
38-
"hono": "^4.12.12"
38+
"hono": "^4.12.12",
39+
"pino": "^10.3.1",
40+
"pino-pretty": "^13.1.3"
3941
},
4042
"devDependencies": {
4143
"@objectstack/cli": "workspace:*",

apps/server/scripts/build-vercel.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ set -euo pipefail
88
# - esbuild bundles server/index.ts → api/_handler.js (self-contained bundle)
99
# - The committed .js wrapper re-exports from _handler.js at runtime
1010
# - Studio SPA is built and copied to public/ for serving the UI
11+
# - External dependencies installed in api/node_modules/ (no symlinks)
1112
#
1213
# Steps:
1314
# 1. Build the project with turbo (includes studio)
1415
# 2. Bundle the API serverless function (→ api/_handler.js)
1516
# 3. Copy studio dist files to public/ for UI serving
17+
# 4. Install external deps in api/node_modules/ (resolve pnpm symlinks)
1618

1719
echo "[build-vercel] Starting server build..."
1820

@@ -36,4 +38,26 @@ else
3638
echo "[build-vercel] ⚠ Studio dist not found (skipped)"
3739
fi
3840

41+
# 4. Install external dependencies in api/node_modules/ (no symlinks)
42+
# pnpm uses symlinks in node_modules/, which Vercel's serverless function
43+
# packaging cannot handle ("invalid deployment package" error).
44+
# We use npm to install external packages as real files next to the handler.
45+
echo "[build-vercel] Installing external dependencies for serverless function..."
46+
cat > api/_package.json << 'DEPS'
47+
{
48+
"private": true,
49+
"dependencies": {
50+
"@libsql/client": "0.14.0",
51+
"pino": "10.3.1",
52+
"pino-pretty": "13.1.3"
53+
}
54+
}
55+
DEPS
56+
cd api
57+
mv _package.json package.json
58+
npm install --production --no-package-lock --ignore-scripts --loglevel error
59+
rm package.json
60+
cd ..
61+
echo "[build-vercel] ✓ External dependencies installed in api/node_modules/"
62+
3963
echo "[build-vercel] Done. Static files in public/, serverless function in api/[[...route]].js → api/_handler.js"

apps/server/scripts/bundle-api.mjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ const EXTERNAL = [
2727
'tedious',
2828
// macOS-only native file watcher
2929
'fsevents',
30+
// LibSQL client — has native bindings, must remain external for Vercel
31+
'@libsql/client',
32+
// Logging libraries - use dynamic require, must be external
33+
'pino',
34+
'pino-pretty',
3035
];
3136

3237
await build({

apps/server/server/index.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
*/
1212

1313
import { ObjectKernel, DriverPlugin, AppPlugin } from '@objectstack/runtime';
14-
import { ObjectQLPlugin } from '@objectstack/objectql';
14+
import { ObjectQLPlugin, ObjectQL } from '@objectstack/objectql';
1515
import { TursoDriver } from '@objectstack/driver-turso';
16+
import { InMemoryDriver } from '@objectstack/driver-memory';
1617
import { createHonoApp } from '@objectstack/hono';
1718
import { AuthPlugin } from '@objectstack/plugin-auth';
1819
import { SecurityPlugin } from '@objectstack/plugin-security';
@@ -62,7 +63,11 @@ async function ensureKernel(): Promise<ObjectKernel> {
6263
// Register ObjectQL engine
6364
await kernel.use(new ObjectQLPlugin());
6465

65-
// Database driver - Turso (remote mode for Vercel)
66+
// Register Memory driver (default for CRM, Todo, BI apps)
67+
const memoryDriver = new InMemoryDriver();
68+
await kernel.use(new DriverPlugin(memoryDriver, 'memory'));
69+
70+
// Register Turso driver (for sys namespace)
6671
const tursoUrl = process.env.TURSO_DATABASE_URL;
6772
const tursoToken = process.env.TURSO_AUTH_TOKEN;
6873

@@ -76,7 +81,7 @@ async function ensureKernel(): Promise<ObjectKernel> {
7681
// Remote mode - no local sync needed for Vercel
7782
});
7883

79-
await kernel.use(new DriverPlugin(tursoDriver));
84+
await kernel.use(new DriverPlugin(tursoDriver, 'turso'));
8085

8186
// Load app manifests (BEFORE plugins that need object schemas)
8287
await kernel.use(new AppPlugin(CrmApp));
@@ -112,6 +117,18 @@ async function ensureKernel(): Promise<ObjectKernel> {
112117

113118
await kernel.bootstrap();
114119

120+
// Configure datasource mapping AFTER bootstrap (ObjectQL service is registered during init)
121+
const ql = await kernel.getServiceAsync<ObjectQL>('objectql');
122+
if (ql && typeof ql.setDatasourceMapping === 'function') {
123+
ql.setDatasourceMapping([
124+
// System objects (sys namespace) use Turso for persistent storage
125+
{ namespace: 'sys', datasource: 'turso' },
126+
// All other objects use Memory driver as default
127+
{ default: true, datasource: 'memory' },
128+
]);
129+
console.log('[Vercel] Datasource mapping configured: sys → turso, default → memory');
130+
}
131+
115132
_kernel = kernel;
116133
console.log('[Vercel] Kernel ready.');
117134
return kernel;
@@ -249,6 +266,5 @@ export default getRequestListener(async (request, env) => {
249266
* Vercel per-function configuration.
250267
*/
251268
export const config = {
252-
memory: 1024,
253269
maxDuration: 60,
254270
};

apps/server/vercel.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
},
1212
"functions": {
1313
"api/**/*.js": {
14-
"memory": 1024,
15-
"maxDuration": 60
14+
"maxDuration": 60,
15+
"includeFiles": "api/node_modules/**"
1616
}
1717
},
1818
"headers": [

pnpm-lock.yaml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)