Vercel Deployment#

BunTok provides a Bun runtime configuration for Vercel. Select "Yes" when prompted during buntok init to create vercel.json.

How It Works#

The generated server.ts entry point starts a local Bun server with app.listen(). A serverless deployment uses the app's fetch handler instead of binding a local port.

server.ts - local server entry point#

typescript
import { app } from "./src/index";
import { env } from "./src/env";

app.listen(env.PORT);

Why `app.listen()` Works Everywhere#

  • Local dev - app.listen() calls Bun.serve() and binds to a port
  • Serverless deployment - expose app.fetch through the deployment adapter instead of binding a local port
  • Keep the local server entry point separate from the serverless adapter.

app.fetch() vs app.listen()#

MethodUse CaseBinds Port
app.listen(port)Local developmentYes
app.fetch(request)Vercel/serverlessNo
app.request(input, init?)TestingNo

vercel.json#

Generated by buntok init when Vercel is selected:

json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "framework": "bun",
  "bunVersion": "1.4.x",
  "outputDirectory": "buntok",
  "regions": ["sin1"]
}
  • framework: "bun" - tells Vercel to use Bun runtime (required for GitHub-triggered deploys).
  • bunVersion: "1.4.x" - pins the Bun runtime version.
  • outputDirectory: "buntok" - build output directory produced by buntok build; required for a successful deploy (the build fails with UNRESOLVED_ENTRY otherwise).
  • regions: ["sin1"] - deploy to the Singapore region (closest to ID users; lower latency).

Deploy#

bash
# Install Vercel CLI
npm i -g vercel

# Deploy (preview)
vercel

# Production deploy
vercel --prod

Local Development#

bash
bun run dev   # runs: bun --watch server.ts

The dev script runs server.ts, which calls app.listen() for local development. A serverless deployment uses app.fetch instead.

Project Structure#

typescript
my-app/
├── src/
│   ├── index.ts              # export const app = new Buntok()
│   ├── env.ts                # Buntok.validateEnv({ PORT, ... })
│   ├── controllers/
│   ├── services/
│   └── repositories/
├── server.ts                 # app.listen(env.PORT) - local server entry point
├── vercel.json               # Vercel config (framework: "bun")
├── package.json
└── ...

Common Issues#

SyntaxError: Unexpected token in import call#

This error occurs when package.json is missing "type": "module". Without it, Vercel's Node.js runtime treats .js files as CommonJS, causing ESM import syntax to fail.

Warning
Fix: Ensure your package.json includes "type": "module". BunTok requires ESM.
json
{
  "name": "my-app",
  "type": "module",
  "scripts": {
    "dev": "bun --watch server.ts",
    "build": "bun run build.ts",
    "start": "node dist/index.js"
  }
}

`cannot read properties of undefined (reading 'readFile')`#

This error occurs when typescript is listed as a peerDependency in your project. Vercel tries to resolve peer dependencies during build, and if the version doesn't exist (e.g., typescript@^7), the TypeScript compiler fails to load.

Warning
Fix: Remove typescript from peerDependencies in your package.json. Keep it only as a devDependency if needed. TypeScript is already included transitively through tsup, @prisma/client, and other build tools.
json
{
  "peerDependencies": {
    "bun": ">=1.2.0"
    // ❌ Don't add "typescript" here
  },
  "devDependencies": {
    "typescript": "^5.0.0"  // ✅ Only as devDependency
  }
}

Build fails with `Module not found`#

Make sure all dependencies are installed before building. Run bun install locally and verify node_modules exists. For Vercel, ensure your installCommand or buildCommand includes dependency installation.

Notes#

  • WebSocket routes require a runtime that supports WebSockets; verify platform support before deploying.
  • For monorepos, set the Root Directory in Vercel dashboard to your project subfolder
Info
Use app.listen() for a local Bun server. Use app.fetch through your platform's serverless adapter when the platform invokes a fetch handler.