Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions apps/web/.dev.vars.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Secrets for `pnpm preview:cloudflare` (wrangler dev). Copy to `.dev.vars`;
# never commit it. In production set each with `wrangler secret put <NAME>`.
POSTGRES_URL=postgresql://root:root@127.0.0.1:5432/vitnode
VITNODE_WEB_URL=http://localhost:8787
CRON_SECRET=your-secure-cron-secret-key
# Optional
SUPABASE_URL=
SUPABASE_SECRET_KEY=
SUPABASE_STORAGE_BUCKET=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
2 changes: 2 additions & 0 deletions apps/web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ dist-ssr
*.local
.env*
!.env.example
.dev.vars*
!.dev.vars.example
.nitro
.tanstack
.wrangler
Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/docs/dev/cli/build.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ vitnode start

| Project | Entry it runs | Default port |
| -------------- | ----------------------------------------------------------------- | ------------ |
| App | Nitro's server entry, usually `.output/server/index.mjs`, on Node | `3000` |
| App | Nitro's server entry, usually `.output/server/index.mjs`, on Node - or on Bun when it was built with Nitro's `bun` preset | `3000` |
| API app (Node) | `dist/index.js` on Node | `8000` |
| API app (Bun) | `src/index.ts` on Bun | `8000` |

Expand Down
1 change: 1 addition & 0 deletions apps/web/content/docs/dev/cli/dev.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ The port is `--port`, then `PORT`, then `8000`. The CLI passes it to your API as

The API runs on Bun, with `bun --hot`, when any of these is true:

- `package.json` declares `"vitnode": { "runtime": "bun" }` (`"node"` there forces Node, whatever else says Bun)
- you started the script with Bun (`bun run dev`)
- the nearest `packageManager` field says `bun@...`
- the nearest lockfile is `bun.lock` or `bun.lockb`
Expand Down
12 changes: 12 additions & 0 deletions apps/web/content/docs/dev/contribution.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ Open `http://localhost:3000` for the website, the AdminCP at `/admin` and the li
| `apps/api` | The standalone Hono API server |
| `plugins/*` | First-party plugins (`@vitnode/blog`, `@vitnode/forum`, `newsroom`) |

### Deploy the website to Cloudflare

`apps/web` builds for Node by default. `pnpm build:cloudflare` builds the same app as one Cloudflare Worker - site, API and realtime hub - with the settings in `apps/web/wrangler.jsonc`. Build the CLI and plugins first, as in [Run the checks](#run-the-checks), then from `apps/web`:

```bash
pnpm build:cloudflare
pnpm preview:cloudflare # local workerd, reads .dev.vars (copy .dev.vars.example)
pnpm deploy:cloudflare
```

Set `POSTGRES_URL`, `VITNODE_WEB_URL` and `CRON_SECRET` once with `npx wrangler secret put <NAME>`, plus the Supabase and Stripe keys the site uses. Leave `REDIS_URL` unset. Run migrations from your machine or CI with `pnpm --filter api db:migrate` against the production database before each deploy. Images are stored as uploaded on Workers, because re-encoding needs `sharp`. [Cloudflare Workers](/docs/dev/deployments/cloud/cloudflare) explains the Worker itself.

## Run the checks

Before opening a pull request, run what GitHub Actions runs, from the repository root:
Expand Down
155 changes: 155 additions & 0 deletions apps/web/content/docs/dev/deployments/bun.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
---
title: Bun
description: Develop, build and run a VitNode app or API on Bun instead of Node.js - TanStack Start through Nitro's bun preset, and Hono on Bun.serve.
icon: Zap
---

import { Tab, Tabs } from 'fumadocs-ui/components/tabs'

VitNode runs on [Bun](https://bun.com) from the first `dev` to production. The TanStack Start app runs Vite on Bun and builds with Nitro's `bun` preset, and a standalone Hono API runs its TypeScript straight on `Bun.serve` - no compile step. Same app, same plugins, one runtime fewer to install on the server.

## Before you begin

- [Bun](https://bun.com/docs/installation) installed on your machine and on the server. Node.js is optional: when it is missing, `bun run` stands in for it, so the `vitnode` CLI still runs.
- A PostgreSQL database, as for any VitNode deployment.

## Create a Bun app

Answer **Bun** to "Where to deploy?" when you create the app. The option shows up only when Bun is installed, and it is the default when you pick Bun as the package manager.

<Tabs groupId="package-manager" persist items={["bun", "pnpm", "npm"]} label="Create a Bun app">

```bash tab="bun"
bun create vitnode-app@canary --deploy bun
```

```bash tab="pnpm"
pnpm create vitnode-app@canary --deploy bun
```

```bash tab="npm"
npx create-vitnode-app@canary --deploy bun
```

</Tabs>

Any package manager can install the packages; the runtime is a separate choice. Here is what the generator changes compared with a Node app:

| Where | Node app | Bun app |
| -------------------- | ---------------------------------- | ----------------------------------------------------- |
| App `dev` / `build` | `vitnode dev`, `vitnode build` | `bun --bun vitnode dev`, `bun --bun vitnode build` |
| App `vite.config.ts` | Nitro's default Node server | `nitro: { preset: "bun" }` |
| API entry | `@hono/node-server` and `ws` | `export default { fetch, websocket }` with `hono/bun` |
| API `build` | `tsc` into `dist` | none - Bun runs `src/index.ts` |
| `package.json` | `"vitnode": { "runtime": "node" }` | `"vitnode": { "runtime": "bun" }` |

`start` stays `vitnode start` in both. It reads the build's preset (or the API's `vitnode.runtime`) and starts the server with Bun or Node to match.

## Move an existing app to Bun

<Steps>
<Step>

### Build the app for Bun

Set Nitro's preset in the app's `vite.config.ts`:

```ts title="vite.config.ts (excerpt)"
vitnode({
nitro: {
preset: 'bun',
rollupConfig: { external: [/^@sentry\//] },
},
webSocket: true,
})
```

</Step>
<Step>

### Run Vite on Bun

Prefix the app's `dev` and `build` scripts with `bun --bun`, so Vite and the SSR code run on Bun rather than on Node:

```json title="package.json (excerpt)"
{
"scripts": {
"dev": "bun --bun vitnode dev",
"build": "bun --bun vitnode build",
"start": "vitnode start"
},
"vitnode": { "runtime": "bun" }
}
```

</Step>
<Step>

### Serve a standalone API with Bun

Skip this step for a single app - its API is part of the build above.

A standalone API exports Bun's server object instead of calling `serve` from `@hono/node-server`. Copy the [WebSocket route for Bun](/docs/dev/websocket/setup#on-bun), drop the API's `build` script and set `"vitnode": { "runtime": "bun" }` in its `package.json`. `vitnode dev` then runs `bun --hot src/index.ts` and `vitnode start` runs `bun src/index.ts`. You can remove `@hono/node-server` and add `@types/bun` to `devDependencies`.

</Step>
</Steps>

## Build and run

Deploy exactly as in [Self-hosted](/docs/dev/deployments/self-hosted), with the same four commands:

<Tabs groupId="package-manager" persist items={["bun", "pnpm", "npm"]} label="Deploy on Bun">

```bash tab="bun"
bun install --frozen-lockfile
bun run build
bun run db:migrate
bun start
```

```bash tab="pnpm"
pnpm install --frozen-lockfile
pnpm build
pnpm db:migrate
pnpm start
```

```bash tab="npm"
npm ci
npm run build
npm run db:migrate
npm start
```

</Tabs>

<Callout type="warn" title="Bun reads .env on its own">
Bun loads `.env` before your code starts, so every variable in it applies -
even one you forgot about. A `REDIS_URL` pointing at a Redis that isn't
running makes WebSocket handshakes answer `503` until Redis is up. Comment out
what you don't use.
</Callout>

## Check the result

`vitnode start` prints `Running` once the server accepts connections. To be sure it is Bun answering, list the processes: the server shows up as `bun .output/server/index.mjs` for an app, or `bun src/index.ts` for an API.

## Related

<Cards>
<Card
title="Self-hosted"
description="Environment variables, Nginx and PM2 for any runtime"
href="/docs/dev/deployments/self-hosted"
/>
<Card
title="WebSocket setup"
description="The /api/ws route on Nitro, Node and Bun"
href="/docs/dev/websocket/setup"
/>
<Card
title="Build and start"
description="What vitnode build and vitnode start do"
href="/docs/dev/cli/build"
/>
</Cards>
Loading
Loading