Skip to content

Commit 0f3ad66

Browse files
Claudehotlong
andauthored
Add examples/vercel for Hono Vercel deployment
- Create complete Vercel deployment example - Add esbuild bundling for serverless functions - Include TypeScript configuration and build scripts - Add comprehensive README with deployment instructions - Configure pnpm for Vercel compatibility Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/dda89e50-471b-46f6-b66d-8ec4d0f19cf4 Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 1dcc09e commit 0f3ad66

11 files changed

Lines changed: 615 additions & 0 deletions

File tree

examples/vercel/.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Dependencies
2+
node_modules
3+
4+
# Build outputs
5+
dist
6+
api/_handler.js
7+
api/_handler.js.map
8+
9+
# Environment variables
10+
.env
11+
.env.local
12+
.env.production
13+
14+
# Vercel
15+
.vercel
16+
17+
# OS
18+
.DS_Store

examples/vercel/.npmrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# pnpm configuration for Vercel deployment
2+
# Use hoisted node_modules to avoid symlink issues in serverless functions
3+
node-linker=hoisted

examples/vercel/README.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Vercel Deployment Example
2+
3+
Deploy an ObjectStack server with Hono to Vercel.
4+
5+
## Features
6+
7+
- ✅ Hono adapter for fast, edge-compatible API routes
8+
- ✅ Turso/LibSQL database driver with in-memory fallback
9+
- ✅ Authentication with better-auth
10+
- ✅ Security plugin for RBAC
11+
- ✅ Optimized serverless function bundling with esbuild
12+
- ✅ Environment-based configuration
13+
14+
## Prerequisites
15+
16+
1. A [Vercel](https://vercel.com) account
17+
2. A [Turso](https://turso.tech) database (optional, uses in-memory storage if not configured)
18+
19+
## Local Development
20+
21+
```bash
22+
# Install dependencies (from monorepo root)
23+
pnpm install
24+
25+
# Start local development server
26+
cd examples/vercel
27+
pnpm dev
28+
```
29+
30+
The server will be available at `http://localhost:3000/api/v1`.
31+
32+
## Deployment to Vercel
33+
34+
### Option 1: Deploy via Vercel CLI
35+
36+
```bash
37+
# Install Vercel CLI
38+
npm i -g vercel
39+
40+
# Deploy from the examples/vercel directory
41+
cd examples/vercel
42+
vercel
43+
```
44+
45+
### Option 2: Deploy via Vercel Dashboard
46+
47+
1. Import your GitHub repository in the [Vercel Dashboard](https://vercel.com/new)
48+
2. Set the **Root Directory** to `examples/vercel`
49+
3. Configure environment variables (see below)
50+
4. Click **Deploy**
51+
52+
## Environment Variables
53+
54+
Configure these in your Vercel project settings:
55+
56+
| Variable | Description | Required | Example |
57+
|----------|-------------|----------|---------|
58+
| `TURSO_DATABASE_URL` | Turso database connection URL | No* | `libsql://your-db.turso.io` |
59+
| `TURSO_AUTH_TOKEN` | Turso authentication token | No* | `eyJ...` |
60+
| `AUTH_SECRET` | Secret key for authentication (min 32 chars) | Yes | Generate with `openssl rand -base64 32` |
61+
62+
*If not set, the server will use an in-memory database (data will be lost on restart).
63+
64+
### Setting up Turso Database
65+
66+
```bash
67+
# Install Turso CLI
68+
curl -sSfL https://get.tur.so/install.sh | bash
69+
70+
# Create a new database
71+
turso db create objectstack-vercel
72+
73+
# Get the database URL
74+
turso db show objectstack-vercel --url
75+
76+
# Create an auth token
77+
turso db tokens create objectstack-vercel
78+
79+
# Add both values to Vercel environment variables
80+
```
81+
82+
## Project Structure
83+
84+
```
85+
examples/vercel/
86+
├── api/
87+
│ └── [[...route]].js # Vercel serverless function entry point
88+
├── scripts/
89+
│ ├── bundle-api.mjs # esbuild bundler for serverless function
90+
│ └── build-vercel.sh # Vercel build script
91+
├── server/
92+
│ └── index.ts # Server entrypoint with kernel bootstrap
93+
├── objectstack.config.ts # ObjectStack configuration
94+
├── package.json
95+
├── tsconfig.json
96+
├── vercel.json # Vercel deployment configuration
97+
└── README.md
98+
```
99+
100+
## How It Works
101+
102+
1. **Build Step**: `scripts/build-vercel.sh` runs on Vercel, which:
103+
- Builds the monorepo using turbo
104+
- Bundles `server/index.ts``api/_handler.js` using esbuild
105+
106+
2. **Runtime**: Vercel routes requests to `api/[[...route]].js`, which:
107+
- Lazily boots the ObjectStack kernel on first request
108+
- Delegates to the Hono adapter for request handling
109+
- Persists kernel state across warm invocations
110+
111+
3. **Database**:
112+
- Production: Uses Turso (edge-compatible LibSQL)
113+
- Local dev: Falls back to in-memory driver
114+
115+
## API Routes
116+
117+
All ObjectStack API routes are available under `/api/v1`:
118+
119+
- `GET /api/v1/meta` - Metadata discovery
120+
- `GET /api/v1/data/:object` - Query data
121+
- `POST /api/v1/data/:object` - Insert records
122+
- `PATCH /api/v1/data/:object/:id` - Update records
123+
- `DELETE /api/v1/data/:object/:id` - Delete records
124+
- `POST /api/v1/auth/sign-in` - Authentication
125+
- And more...
126+
127+
## Testing the Deployment
128+
129+
```bash
130+
# Health check
131+
curl https://your-deployment.vercel.app/api/v1/meta
132+
133+
# Example API request (after authentication)
134+
curl https://your-deployment.vercel.app/api/v1/data/users \
135+
-H "Authorization: Bearer YOUR_TOKEN"
136+
```
137+
138+
## Troubleshooting
139+
140+
### Build fails with "Module not found"
141+
142+
Make sure you're running the build from the monorepo root, or that Vercel's `installCommand` is set correctly in `vercel.json`.
143+
144+
### Database connection issues
145+
146+
- Verify `TURSO_DATABASE_URL` and `TURSO_AUTH_TOKEN` are set correctly
147+
- Check Turso database is accessible from Vercel's network
148+
- For debugging, you can temporarily use `:memory:` as the database URL
149+
150+
### Cold start timeout
151+
152+
- Increase `maxDuration` in `vercel.json` if needed
153+
- Consider using Vercel Pro for higher limits
154+
155+
## Learn More
156+
157+
- [ObjectStack Documentation](https://docs.objectstack.dev)
158+
- [Hono Vercel Deployment Guide](https://vercel.com/docs/frameworks/backend/hono)
159+
- [Turso Documentation](https://docs.turso.tech)
160+
- [Vercel Serverless Functions](https://vercel.com/docs/functions/serverless-functions)
161+
162+
## License
163+
164+
Apache-2.0
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Vercel Serverless Function — Catch-all API route.
2+
//
3+
// This file MUST be committed to the repository so Vercel can detect it
4+
// as a serverless function during the pre-build phase.
5+
//
6+
// It delegates to the esbuild bundle (`_handler.js`) generated by
7+
// `scripts/bundle-api.mjs` during the Vercel build step.
8+
9+
export { default, config } from './_handler.js';
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import { defineStack } from '@objectstack/spec';
4+
import { AppPlugin, DriverPlugin } from '@objectstack/runtime';
5+
import { ObjectQLPlugin } from '@objectstack/objectql';
6+
import { TursoDriver } from '@objectstack/driver-turso';
7+
import { InMemoryDriver } from '@objectstack/driver-memory';
8+
import { AuthPlugin } from '@objectstack/plugin-auth';
9+
import { SecurityPlugin } from '@objectstack/plugin-security';
10+
import { MetadataPlugin } from '@objectstack/metadata';
11+
12+
/**
13+
* Vercel Deployment Example
14+
*
15+
* This example demonstrates how to deploy an ObjectStack server to Vercel
16+
* using the Hono adapter. It includes:
17+
*
18+
* - TursoDriver for production (with fallback to in-memory for local dev)
19+
* - Authentication with better-auth (environment-based configuration)
20+
* - Security plugin for RBAC
21+
* - Metadata plugin for runtime metadata management
22+
*
23+
* Environment Variables (set in Vercel dashboard or .env.local):
24+
* - TURSO_DATABASE_URL: Turso database connection URL (or ":memory:" for local)
25+
* - TURSO_AUTH_TOKEN: Turso authentication token (optional for local)
26+
* - AUTH_SECRET: Secret key for authentication (min 32 characters)
27+
* - VERCEL_URL: Auto-injected by Vercel (deployment URL)
28+
* - VERCEL_PROJECT_PRODUCTION_URL: Auto-injected by Vercel (production URL)
29+
*/
30+
31+
// Determine if we're running in production (Vercel) or local dev
32+
const isProduction = process.env.VERCEL === '1';
33+
34+
// Database driver: Use Turso in production, in-memory for local dev
35+
const driver = isProduction || process.env.TURSO_DATABASE_URL
36+
? new TursoDriver({
37+
url: process.env.TURSO_DATABASE_URL ?? ':memory:',
38+
...(process.env.TURSO_AUTH_TOKEN && { authToken: process.env.TURSO_AUTH_TOKEN }),
39+
})
40+
: new InMemoryDriver();
41+
42+
// Base URL for authentication (auto-detected from Vercel environment)
43+
const baseUrl = process.env.VERCEL_PROJECT_PRODUCTION_URL
44+
? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
45+
: process.env.VERCEL_URL
46+
? `https://${process.env.VERCEL_URL}`
47+
: 'http://localhost:3000';
48+
49+
// Collect trusted origins for CORS and CSRF protection
50+
function getVercelOrigins(): string[] {
51+
const origins: string[] = [];
52+
if (process.env.VERCEL_URL) {
53+
origins.push(`https://${process.env.VERCEL_URL}`);
54+
}
55+
if (process.env.VERCEL_BRANCH_URL) {
56+
origins.push(`https://${process.env.VERCEL_BRANCH_URL}`);
57+
}
58+
if (process.env.VERCEL_PROJECT_PRODUCTION_URL) {
59+
origins.push(`https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`);
60+
}
61+
return origins;
62+
}
63+
64+
const trustedOrigins = getVercelOrigins();
65+
66+
export default defineStack({
67+
manifest: {
68+
id: 'com.example.vercel',
69+
namespace: 'vercel',
70+
name: 'Vercel Deployment Example',
71+
version: '1.0.0',
72+
description: 'Example application demonstrating Hono deployment to Vercel',
73+
type: 'app',
74+
},
75+
76+
// Core plugins required for a functional ObjectStack server
77+
plugins: [
78+
new ObjectQLPlugin(),
79+
new DriverPlugin(driver),
80+
new AuthPlugin({
81+
secret: process.env.AUTH_SECRET ?? 'dev-secret-please-change-in-production-min-32-chars',
82+
baseUrl,
83+
...(trustedOrigins.length > 0 ? { trustedOrigins } : {}),
84+
}),
85+
new SecurityPlugin(),
86+
new MetadataPlugin({ watch: false }), // Disable file watching on Vercel
87+
],
88+
});

examples/vercel/package.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "@example/vercel",
3+
"version": "4.0.3",
4+
"description": "Example: Deploy ObjectStack server with Hono to Vercel",
5+
"license": "Apache-2.0",
6+
"private": true,
7+
"type": "module",
8+
"scripts": {
9+
"dev": "hono dev",
10+
"build": "bash scripts/build-vercel.sh",
11+
"typecheck": "tsc --noEmit"
12+
},
13+
"dependencies": {
14+
"@hono/node-server": "^1.19.14",
15+
"@objectstack/core": "workspace:*",
16+
"@objectstack/driver-memory": "workspace:*",
17+
"@objectstack/driver-turso": "workspace:*",
18+
"@objectstack/hono": "workspace:*",
19+
"@objectstack/metadata": "workspace:*",
20+
"@objectstack/objectql": "workspace:*",
21+
"@objectstack/plugin-auth": "workspace:*",
22+
"@objectstack/plugin-security": "workspace:*",
23+
"@objectstack/runtime": "workspace:*",
24+
"@objectstack/spec": "workspace:*",
25+
"hono": "^4.12.12"
26+
},
27+
"devDependencies": {
28+
"@types/node": "^22.14.3",
29+
"esbuild": "^0.28.0",
30+
"typescript": "^6.0.2"
31+
}
32+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Build script for Vercel deployment of ObjectStack server with Hono.
5+
#
6+
# This script:
7+
# 1. Builds the monorepo from the root using turbo
8+
# 2. Bundles the serverless function using esbuild
9+
#
10+
# The bundled function is self-contained and ready for Vercel deployment.
11+
12+
echo "[build-vercel] Starting build..."
13+
14+
# 1. Build the monorepo from the root
15+
cd ../..
16+
pnpm turbo run build --filter=@example/vercel
17+
cd examples/vercel
18+
19+
# 2. Bundle API serverless function
20+
node scripts/bundle-api.mjs
21+
22+
echo "[build-vercel] Done. Serverless function ready at api/_handler.js"
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Pre-bundles the Vercel serverless API function.
3+
*
4+
* This script bundles server/index.ts with dependencies inlined,
5+
* creating a self-contained serverless function for Vercel deployment.
6+
*
7+
* Native packages like better-sqlite3 are kept external and will be
8+
* packaged separately by Vercel.
9+
*/
10+
11+
import { build } from 'esbuild';
12+
13+
// Packages that cannot be bundled (native bindings / optional drivers)
14+
const EXTERNAL = [
15+
'better-sqlite3',
16+
'@libsql/client',
17+
// Optional knex database drivers
18+
'pg',
19+
'pg-native',
20+
'pg-query-stream',
21+
'mysql',
22+
'mysql2',
23+
'sqlite3',
24+
'oracledb',
25+
'tedious',
26+
// macOS-only native file watcher
27+
'fsevents',
28+
];
29+
30+
await build({
31+
entryPoints: ['server/index.ts'],
32+
bundle: true,
33+
platform: 'node',
34+
format: 'esm',
35+
target: 'es2020',
36+
outfile: 'api/_handler.js',
37+
sourcemap: true,
38+
external: EXTERNAL,
39+
logOverride: { 'require-resolve-not-external': 'silent' },
40+
banner: {
41+
js: [
42+
'// Bundled by esbuild — see scripts/bundle-api.mjs',
43+
'import { createRequire } from "module";',
44+
'const require = createRequire(import.meta.url);',
45+
].join('\n'),
46+
},
47+
});
48+
49+
console.log('[bundle-api] Bundled server/index.ts → api/_handler.js');

0 commit comments

Comments
 (0)