-
Notifications
You must be signed in to change notification settings - Fork 1
feat: add Vercel deployment support to app-host example using Hono #1120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
33c7ce3
feat: add Vercel deployment support to app-host example using Hono
Claude 4759412
docs: add deployment documentation and Vercel deploy button
Claude c5424cd
fix: exclude bundled API handler from git (build artifact)
Claude 60db1ea
fix: update better-sqlite3 dependency to version 12.9.0
hotlong 4a6cc73
Merge branch 'main' into claude/deploy-example-app-host-vercel
hotlong fbf4686
fix: set framework to "hono" in vercel.json per Vercel Hono docs
Claude 6c106d1
fix: use TursoDriver instead of InMemoryDriver, remove unnecessary be…
Claude ee32099
fix: address PR review feedback - add type: module, fix .vercelignore…
Claude 0d115e8
chore: update pnpm-lock.yaml after dependency changes
Claude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Turso Database Configuration | ||
| # Required for Vercel deployment | ||
| TURSO_DATABASE_URL=libsql://your-database.turso.io | ||
| TURSO_AUTH_TOKEN=your-auth-token-here |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # Build artifacts | ||
| dist/ | ||
| .turbo/ | ||
|
|
||
| # Bundled API handler (generated during Vercel build) | ||
| api/_handler.js | ||
| api/_handler.js.map | ||
|
|
||
| # Node modules | ||
| node_modules/ | ||
|
|
||
| # Environment files | ||
| .env | ||
| .env.local | ||
| .env.*.local | ||
|
|
||
| # OS files | ||
| .DS_Store | ||
| Thumbs.db |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # Ignore build artifacts | ||
| dist/ | ||
| .turbo/ | ||
|
|
||
| # Ignore test files | ||
| test/ | ||
| *.test.ts | ||
| *.spec.ts | ||
|
|
||
| # Ignore development-only files that are not required by the Vercel build | ||
| debug-registry.ts | ||
|
|
||
| # Keep only the bundled API handler | ||
| !api/_handler.js | ||
| !api/_handler.js.map | ||
| !api/[[...route]].js |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| # Deploying App Host to Vercel | ||
|
|
||
| This example demonstrates how to deploy the ObjectStack app-host to Vercel using Hono. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| 1. A Vercel account | ||
| 2. Vercel CLI installed (optional): `npm i -g vercel` | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| Set the following environment variables in your Vercel project: | ||
|
|
||
| - `AUTH_SECRET`: A secure random string (minimum 32 characters) for authentication | ||
| - `TURSO_DATABASE_URL`: Your Turso database URL (e.g., `libsql://your-database.turso.io`) | ||
| - `TURSO_AUTH_TOKEN`: Your Turso authentication token | ||
|
|
||
| You can get these credentials from [Turso Dashboard](https://turso.tech/). | ||
|
|
||
| ## Deployment Steps | ||
|
|
||
| ### Option 1: Using Vercel CLI | ||
|
|
||
| 1. Install Vercel CLI: | ||
| ```bash | ||
| npm i -g vercel | ||
| ``` | ||
|
|
||
| 2. Navigate to the app-host directory: | ||
| ```bash | ||
| cd examples/app-host | ||
| ``` | ||
|
|
||
| 3. Deploy to Vercel: | ||
| ```bash | ||
| vercel | ||
| ``` | ||
|
|
||
| 4. Set environment variables: | ||
| ```bash | ||
| vercel env add AUTH_SECRET | ||
| vercel env add TURSO_DATABASE_URL | ||
| vercel env add TURSO_AUTH_TOKEN | ||
| ``` | ||
|
|
||
| ### Option 2: Using Vercel Dashboard | ||
|
|
||
| 1. Import the repository to Vercel | ||
| 2. Set the root directory to `examples/app-host` | ||
| 3. Add environment variables in the project settings | ||
| 4. Deploy | ||
|
|
||
| ## Build Configuration | ||
|
|
||
| The build is configured in `vercel.json`: | ||
|
|
||
| - **Install Command**: `cd ../.. && pnpm install` (installs monorepo dependencies) | ||
| - **Build Command**: `bash scripts/build-vercel.sh` (builds and bundles the application) | ||
| - **Framework**: `hono` (uses Vercel's Hono framework preset) | ||
|
|
||
| ## How It Works | ||
|
|
||
| 1. **Build Process** (`scripts/build-vercel.sh`): | ||
| - Builds the TypeScript project using Turbo | ||
| - Bundles the server code using esbuild (`scripts/bundle-api.mjs`) | ||
|
|
||
| 2. **API Handler** (`api/[[...route]].js`): | ||
| - Committed catch-all route handler that Vercel detects pre-build | ||
| - Delegates to the bundled handler (`api/_handler.js`) | ||
|
|
||
| 3. **Server Entrypoint** (`server/index.ts`): | ||
| - Boots ObjectStack kernel with Hono adapter | ||
| - Uses `@hono/node-server`'s `getRequestListener()` for Vercel compatibility | ||
| - Connects to Turso database in remote mode (HTTP-only, no local SQLite) | ||
| - Handles Vercel's pre-buffered request body properly | ||
|
|
||
| 4. **Hono Integration**: | ||
| - Uses `@objectstack/hono` adapter to create the HTTP application | ||
| - Provides REST API at `/api/v1` prefix | ||
| - Includes authentication via AuthPlugin | ||
|
|
||
| ## Architecture | ||
|
|
||
| The deployment follows Vercel's serverless function pattern: | ||
|
|
||
| ``` | ||
| examples/app-host/ | ||
| ├── api/ | ||
| │ ├── [[...route]].js # Committed entry point | ||
| │ └── _handler.js # Generated bundle (not committed) | ||
| ├── server/ | ||
| │ └── index.ts # Server implementation | ||
| ├── scripts/ | ||
| │ ├── build-vercel.sh # Build script | ||
| │ └── bundle-api.mjs # Bundler configuration | ||
| ├── .npmrc # pnpm configuration (node-linker=hoisted) | ||
| └── vercel.json # Vercel configuration | ||
| ``` | ||
|
|
||
| ## Testing Locally | ||
|
|
||
| Before deploying, you can test locally: | ||
|
|
||
| ```bash | ||
| # Build the application | ||
| pnpm build | ||
|
|
||
| # Run in development mode | ||
| pnpm dev | ||
|
|
||
| # Test the API | ||
| curl http://localhost:3000/api/v1/discovery | ||
| ``` | ||
|
|
||
| ## Accessing the API | ||
|
|
||
| After deployment, your API will be available at: | ||
|
|
||
| - Discovery: `https://your-app.vercel.app/api/v1/discovery` | ||
| - Data API: `https://your-app.vercel.app/api/v1/data/:object` | ||
| - Meta API: `https://your-app.vercel.app/api/v1/meta/:type` | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Build Fails | ||
|
|
||
| - Ensure all dependencies are installed: `pnpm install` | ||
| - Check build logs in Vercel dashboard | ||
| - Verify `build-vercel.sh` is executable: `chmod +x scripts/build-vercel.sh` | ||
|
|
||
| ### Runtime Errors | ||
|
|
||
| - Check function logs in Vercel dashboard | ||
| - Verify environment variables are set correctly (`TURSO_DATABASE_URL`, `TURSO_AUTH_TOKEN`, `AUTH_SECRET`) | ||
| - Ensure `AUTH_SECRET` is at least 32 characters | ||
| - Test Turso connection using the provided credentials | ||
|
|
||
| ### Database Connection Issues | ||
|
|
||
| - Verify your Turso database URL and auth token are correct | ||
| - Check that your Turso database is accessible (not paused) | ||
| - The deployment uses TursoDriver in **remote mode** (HTTP-only), which doesn't require native modules like better-sqlite3 | ||
|
|
||
| ## References | ||
|
|
||
| - [Vercel Hono Documentation](https://vercel.com/docs/frameworks/backend/hono) | ||
| - [ObjectStack Documentation](../../README.md) | ||
| - [Hono Documentation](https://hono.dev/) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| // Vercel Serverless Function — Catch-all API route. | ||
| // | ||
| // This file MUST be committed to the repository so Vercel can detect it | ||
| // as a serverless function during the pre-build phase. | ||
| // | ||
| // It delegates to the esbuild bundle (`_handler.js`) generated by | ||
| // `scripts/bundle-api.mjs` during the Vercel build step. A separate | ||
| // bundle file is used (rather than overwriting this file) so that: | ||
| // 1. Vercel always finds this committed entry point (no "File not found"). | ||
| // 2. Vercel does not TypeScript-compile a .ts stub that references | ||
| // source files absent at runtime (no ERR_MODULE_NOT_FOUND). | ||
| // | ||
| // @see ../server/index.ts — the actual server entrypoint | ||
| // @see ../scripts/bundle-api.mjs — the esbuild bundler | ||
|
|
||
| export { default, config } from './_handler.js'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| # Build script for Vercel deployment of @example/app-host. | ||
| # | ||
| # Follows the Vercel deployment pattern: | ||
| # - api/[[...route]].js is committed to the repo (Vercel detects it pre-build) | ||
| # - esbuild bundles server/index.ts → api/_handler.js (self-contained bundle) | ||
| # - The committed .js wrapper re-exports from _handler.js at runtime | ||
| # | ||
| # Steps: | ||
| # 1. Build the project with turbo | ||
| # 2. Bundle the API serverless function (→ api/_handler.js) | ||
| # 3. Copy native/external modules into local node_modules for Vercel packaging | ||
|
|
||
| echo "[build-vercel] Starting app-host build..." | ||
|
|
||
| # 1. Build the project with turbo (from monorepo root) | ||
| cd ../.. | ||
| pnpm turbo run build --filter=@example/app-host | ||
| cd examples/app-host | ||
|
|
||
| # 2. Bundle API serverless function | ||
| node scripts/bundle-api.mjs | ||
|
|
||
| echo "[build-vercel] Done. Serverless function in api/[[...route]].js → api/_handler.js" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| /** | ||
| * Pre-bundles the Vercel serverless API function. | ||
| * | ||
| * Vercel's @vercel/node builder resolves pnpm workspace packages via symlinks, | ||
| * which can cause esbuild to resolve to TypeScript source files rather than | ||
| * compiled dist output — producing ERR_MODULE_NOT_FOUND at runtime. | ||
| * | ||
| * This script bundles server/index.ts with ALL dependencies inlined (including | ||
| * npm packages), so the deployed function is self-contained. Only packages | ||
| * with native bindings are kept external. | ||
| * | ||
| * Run from the examples/app-host directory during the Vercel build step. | ||
| */ | ||
|
|
||
| import { build } from 'esbuild'; | ||
|
|
||
| // Packages that cannot be bundled (native bindings / optional drivers) | ||
| const EXTERNAL = [ | ||
| // Optional knex database drivers — never used at runtime, but knex requires() them | ||
| 'pg', | ||
| 'pg-native', | ||
| 'pg-query-stream', | ||
| 'mysql', | ||
| 'mysql2', | ||
| 'sqlite3', | ||
| 'oracledb', | ||
| 'tedious', | ||
| // macOS-only native file watcher | ||
| 'fsevents', | ||
| ]; | ||
|
|
||
| await build({ | ||
| entryPoints: ['server/index.ts'], | ||
| bundle: true, | ||
| platform: 'node', | ||
| format: 'esm', | ||
| target: 'es2020', | ||
| outfile: 'api/_handler.js', | ||
| sourcemap: true, | ||
| external: EXTERNAL, | ||
| // Silence warnings about optional/unused require() calls in knex drivers | ||
| logOverride: { 'require-resolve-not-external': 'silent' }, | ||
| // Vercel resolves ESM .js files correctly when "type": "module" is set. | ||
| // CJS format would conflict with the project's "type": "module" setting, | ||
| // causing Node.js to fail parsing require()/module.exports as ESM syntax. | ||
| // | ||
| // The createRequire banner provides a real `require` function in the ESM | ||
| // scope. esbuild's __require shim (generated for CJS→ESM conversion) | ||
| // checks `typeof require !== "undefined"` and uses it when available, | ||
| // which fixes "Dynamic require of <builtin> is not supported" errors | ||
| // from CJS dependencies like knex/tarn that require() Node.js built-ins. | ||
| banner: { | ||
| js: [ | ||
| '// Bundled by esbuild — see scripts/bundle-api.mjs', | ||
| 'import { createRequire } from "module";', | ||
| 'const require = createRequire(import.meta.url);', | ||
| ].join('\n'), | ||
| }, | ||
| }); | ||
|
|
||
| console.log('[bundle-api] Bundled server/index.ts → api/_handler.js'); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new Vercel function entrypoint (
api/[[...route]].js) and the bundled handler are authored/emitted as ESM (export ...).@example/app-host/package.jsoncurrently has no"type": "module", so Node/Vercel will treat.jsfiles as CommonJS and throw a syntax error when loading the handler. Add"type": "module"(matchingapps/studio) or switch the Vercel handler/bundle output to CommonJS consistently.