mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-15 20:17:22 +00:00
update cloudflare-vite-code readme to reflect code-only mode
The template documentation now emphasizes code-first approach with manual type management instead of automatic file generation. Includes updated configuration examples, deployment instructions, and a new comparison table between code and hybrid modes.
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
# bknd starter: Cloudflare Vite Hybrid
|
||||
A fullstack React + Vite application with bknd integration, showcasing **hybrid mode** and Cloudflare Workers deployment.
|
||||
# bknd starter: Cloudflare Vite Code-Only
|
||||
A fullstack React + Vite application with bknd integration, showcasing **code-only mode** and Cloudflare Workers deployment.
|
||||
|
||||
## Key Features
|
||||
|
||||
This example demonstrates several advanced bknd features:
|
||||
This example demonstrates a minimal, code-first approach to building with bknd:
|
||||
|
||||
### 🔄 Hybrid Mode
|
||||
Configure your backend **visually in development** using the Admin UI, then automatically switch to **code-only mode in production** for maximum performance. Changes made in the Admin UI are automatically synced to `bknd-config.json` and type definitions are generated in `bknd-types.d.ts`.
|
||||
### 💻 Code-Only Mode
|
||||
Define your entire backend **programmatically** using a Drizzle-like API. Your data structure, authentication, and configuration live directly in code with zero build-time tooling required. Perfect for developers who prefer traditional code-first workflows.
|
||||
|
||||
### 📁 Filesystem Access with Vite Plugin
|
||||
Cloudflare's Vite plugin uses `unenv` which disables Node.js APIs like `fs`. This example uses bknd's `devFsVitePlugin` and `devFsWrite` to provide filesystem access during development, enabling automatic syncing of types and configuration.
|
||||
### 🎯 Minimal Boilerplate
|
||||
Unlike the hybrid mode template, this example uses **no automatic type generation**, **no filesystem plugins**, and **no auto-synced configuration files**. This simulates a typical development environment where you manage types generation manually. If you prefer automatic type generation, you can easily add it using the [CLI](https://docs.bknd.io/usage/cli#generating-types-types) or [Vite plugin](https://docs.bknd.io/extending/plugins#synctypes).
|
||||
|
||||
### ⚡ Split Configuration Pattern
|
||||
- **`config.ts`**: Shared configuration that can be safely imported in your worker
|
||||
- **`config.ts`**: Main configuration that defines your schema and can be safely imported in your worker
|
||||
- **`bknd.config.ts`**: Wraps the configuration with `withPlatformProxy` for CLI usage with Cloudflare bindings (should NOT be imported in your worker)
|
||||
|
||||
This pattern prevents bundling `wrangler` into your worker while still allowing CLI access to Cloudflare resources.
|
||||
@@ -32,67 +32,84 @@ Inside of your project, you'll see the following folders and files:
|
||||
│ │ └── main.tsx
|
||||
│ └── worker/
|
||||
│ └── index.ts # Cloudflare Worker entry
|
||||
├── config.ts # Shared bknd configuration (hybrid mode)
|
||||
├── config.ts # bknd configuration with schema definition
|
||||
├── bknd.config.ts # CLI configuration with platform proxy
|
||||
├── bknd-config.json # Auto-generated production config
|
||||
├── bknd-types.d.ts # Auto-generated TypeScript types
|
||||
├── .env.example # Auto-generated secrets template
|
||||
├── vite.config.ts # Includes devFsVitePlugin
|
||||
├── seed.ts # Optional: seed data for development
|
||||
├── vite.config.ts # Standard Vite config (no bknd plugins)
|
||||
├── package.json
|
||||
└── wrangler.json # Cloudflare Workers configuration
|
||||
```
|
||||
|
||||
## Cloudflare resources
|
||||
## Cloudflare Resources
|
||||
|
||||
- **D1:** `wrangler.json` declares a `DB` binding. In production, replace `database_id` with your own (`wrangler d1 create <name>`).
|
||||
- **R2:** Optional `BUCKET` binding is pre-configured to show how to add additional services.
|
||||
- **Environment awareness:** `ENVIRONMENT` switch toggles hybrid behavior: production makes the database read-only, while development keeps `mode: "db"` and auto-syncs schema.
|
||||
- **Environment awareness:** `ENVIRONMENT` variable determines whether to sync the database schema automatically (development only).
|
||||
- **Static assets:** The Assets binding points to `dist/client`. Run `npm run build` before `wrangler deploy` to upload the client bundle alongside the worker.
|
||||
|
||||
## Admin UI & frontend
|
||||
## Admin UI & Frontend
|
||||
|
||||
- `/admin` mounts `<Admin />` from `bknd/ui` with `withProvider={{ user }}` so it respects the authenticated user returned by `useAuth`.
|
||||
- `/` showcases `useEntityQuery("todos")`, mutation helpers, and authentication state — demonstrating how the generated client types (`bknd-types.d.ts`) flow into the React code.
|
||||
- `/` showcases `useEntityQuery("todos")`, mutation helpers, and authentication state — demonstrating how manually declared types flow into the React code.
|
||||
|
||||
|
||||
## Configuration Files
|
||||
|
||||
### `config.ts`
|
||||
The main configuration file that uses the `hybrid()` mode helper:
|
||||
|
||||
- Loads the generated config via an ESM `reader` (importing `./bknd-config.json`).
|
||||
- Uses `devFsWrite` as the `writer` so the CLI/plugin can persist files even though Node's `fs` API is unavailable in Miniflare.
|
||||
- Sets `typesFilePath`, `configFilePath`, and `syncSecrets` (writes `.env.example`) so config, types, and secret placeholders stay aligned.
|
||||
- Seeds example data/users in `options.seed` when the database is empty.
|
||||
- Disables the built-in admin controller because the React app renders `/admin` via `bknd/ui`.
|
||||
|
||||
The main configuration file that uses the `code()` mode helper:
|
||||
|
||||
```typescript
|
||||
import { hybrid } from "bknd/modes";
|
||||
import { devFsWrite, type CloudflareBkndConfig } from "bknd/adapter/cloudflare";
|
||||
import type { CloudflareBkndConfig } from "bknd/adapter/cloudflare";
|
||||
import { code } from "bknd/modes";
|
||||
import { boolean, em, entity, text } from "bknd";
|
||||
|
||||
export default hybrid<CloudflareBkndConfig>({
|
||||
// Special reader for Cloudflare Workers (no Node.js fs)
|
||||
reader: async () => (await import("./bknd-config.json")).default,
|
||||
// devFsWrite enables file writing via Vite plugin
|
||||
writer: devFsWrite,
|
||||
// Auto-sync these files in development
|
||||
typesFilePath: "./bknd-types.d.ts",
|
||||
configFilePath: "./bknd-config.json",
|
||||
syncSecrets: {
|
||||
enabled: true,
|
||||
outFile: ".env.example",
|
||||
format: "env",
|
||||
},
|
||||
// define your schema using a Drizzle-like API
|
||||
const schema = em({
|
||||
todos: entity("todos", {
|
||||
title: text(),
|
||||
done: boolean(),
|
||||
}),
|
||||
});
|
||||
|
||||
// register your schema for type completion (optional)
|
||||
// alternatively, you can use the CLI to auto-generate types
|
||||
type Database = (typeof schema)["DB"];
|
||||
declare module "bknd" {
|
||||
interface DB extends Database {}
|
||||
}
|
||||
|
||||
export default code<CloudflareBkndConfig>({
|
||||
app: (env) => ({
|
||||
adminOptions: false, // Disabled - we render React app instead
|
||||
config: {
|
||||
// convert schema to JSON format
|
||||
data: schema.toJSON(),
|
||||
auth: {
|
||||
enabled: true,
|
||||
jwt: {
|
||||
// secrets are directly passed to the config
|
||||
secret: env.JWT_SECRET,
|
||||
issuer: "cloudflare-vite-code-example",
|
||||
},
|
||||
},
|
||||
},
|
||||
// disable the built-in admin controller (we render our own app)
|
||||
adminOptions: false,
|
||||
// determines whether the database should be automatically synced
|
||||
isProduction: env.ENVIRONMENT === "production",
|
||||
secrets: env,
|
||||
// ... your configuration
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
Key differences from hybrid mode:
|
||||
- **No auto-generated files**: No `bknd-config.json`, `bknd-types.d.ts`, or `.env.example`
|
||||
- **Manual type declaration**: Types are declared inline using `declare module "bknd"`
|
||||
- **Direct secret access**: Secrets come directly from `env` parameters
|
||||
- **Simpler setup**: No filesystem plugins or readers/writers needed
|
||||
|
||||
If you prefer automatic type generation, you can add it later using:
|
||||
- **CLI**: `npm run bknd -- types` (requires adding `typesFilePath` to config)
|
||||
- **Plugin**: Import `syncTypes` plugin and configure it in your app
|
||||
|
||||
### `bknd.config.ts`
|
||||
Wraps the configuration for CLI usage with Cloudflare bindings:
|
||||
|
||||
@@ -100,21 +117,24 @@ Wraps the configuration for CLI usage with Cloudflare bindings:
|
||||
import { withPlatformProxy } from "bknd/adapter/cloudflare/proxy";
|
||||
import config from "./config.ts";
|
||||
|
||||
export default withPlatformProxy(config);
|
||||
export default withPlatformProxy(config, {
|
||||
useProxy: true,
|
||||
});
|
||||
```
|
||||
|
||||
**Important**: Don't import this file in your worker, as it would bundle `wrangler` into your production code. This file is only used by the bknd CLI.
|
||||
|
||||
### `vite.config.ts`
|
||||
Includes the `devFsVitePlugin` for filesystem access:
|
||||
Standard Vite configuration without bknd-specific plugins:
|
||||
|
||||
```typescript
|
||||
import { devFsVitePlugin } from "bknd/adapter/cloudflare";
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { cloudflare } from "@cloudflare/vite-plugin";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
// ...
|
||||
devFsVitePlugin({ configFile: "config.ts" }),
|
||||
cloudflare(),
|
||||
],
|
||||
plugins: [react(), tailwindcss(), cloudflare()],
|
||||
});
|
||||
```
|
||||
|
||||
@@ -124,13 +144,13 @@ All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
|:-------------------|:----------------------------------------------------------|
|
||||
| `npm install` | Installs dependencies and generates wrangler types |
|
||||
| `npm install` | Installs dependencies, generates types, and seeds database|
|
||||
| `npm run dev` | Starts local dev server with Vite at `localhost:5173` |
|
||||
| `npm run build` | Builds the application for production |
|
||||
| `npm run preview` | Builds and previews the production build locally |
|
||||
| `npm run deploy` | Builds, syncs the schema and deploys to Cloudflare Workers|
|
||||
| `npm run bknd` | Runs bknd CLI commands |
|
||||
| `npm run bknd:types` | Generates TypeScript types from your schema |
|
||||
| `npm run bknd:seed`| Seeds the database with example data |
|
||||
| `npm run cf:types` | Generates Cloudflare Worker types from `wrangler.json` |
|
||||
| `npm run check` | Type checks and does a dry-run deployment |
|
||||
|
||||
@@ -140,41 +160,58 @@ All commands are run from the root of the project, from a terminal:
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
This will install dependencies, generate Cloudflare types, and seed the database.
|
||||
|
||||
2. **Start development server:**
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
3. **Visit the Admin UI** at `http://localhost:5173/admin` to configure your backend visually:
|
||||
- Create entities and fields
|
||||
- Configure authentication
|
||||
- Set up relationships
|
||||
- Define permissions
|
||||
3. **Define your schema in code** (`config.ts`):
|
||||
```typescript
|
||||
const schema = em({
|
||||
todos: entity("todos", {
|
||||
title: text(),
|
||||
done: boolean(),
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
4. **Watch for auto-generated files:**
|
||||
- `bknd-config.json` - Production configuration
|
||||
- `bknd-types.d.ts` - TypeScript types
|
||||
- `.env.example` - Required secrets
|
||||
4. **Manually declare types** (optional, but recommended for IDE support):
|
||||
```typescript
|
||||
type Database = (typeof schema)["DB"];
|
||||
declare module "bknd" {
|
||||
interface DB extends Database {}
|
||||
}
|
||||
```
|
||||
|
||||
5. **Use the CLI** for manual operations:
|
||||
```sh
|
||||
# Generate types manually
|
||||
npm run bknd:types
|
||||
5. **Use the Admin UI** at `http://localhost:5173/admin` to:
|
||||
- View and manage your data
|
||||
- Monitor authentication
|
||||
- Access database tools
|
||||
|
||||
# Sync the production database schema (only safe operations are applied)
|
||||
Note: In code mode, you cannot edit the schema through the UI. All schema changes must be done in `config.ts`.
|
||||
|
||||
6. **Sync schema changes** to your database:
|
||||
```sh
|
||||
# Local development (happens automatically on startup)
|
||||
npm run dev
|
||||
|
||||
# Production database (safe operations only)
|
||||
CLOUDFLARE_ENV=production npm run bknd -- sync --force
|
||||
```
|
||||
|
||||
## Before you deploy
|
||||
## Before You Deploy
|
||||
|
||||
If you're using a D1 database, make sure to create a database in your Cloudflare account and replace the `database_id` accordingly in `wrangler.json`:
|
||||
### 1. Create a D1 Database
|
||||
|
||||
Create a database in your Cloudflare account:
|
||||
|
||||
```sh
|
||||
npx wrangler d1 create my-database
|
||||
```
|
||||
|
||||
Update `wrangler.json`:
|
||||
Update `wrangler.json` with your database ID:
|
||||
```json
|
||||
{
|
||||
"d1_databases": [
|
||||
@@ -187,6 +224,21 @@ Update `wrangler.json`:
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Set Required Secrets
|
||||
|
||||
Set your secrets in Cloudflare Workers:
|
||||
|
||||
```sh
|
||||
# JWT secret (required for authentication)
|
||||
npx wrangler secret put JWT_SECRET
|
||||
```
|
||||
|
||||
You can generate a secure secret using:
|
||||
```sh
|
||||
# Using openssl
|
||||
openssl rand -base64 64
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
Deploy to Cloudflare Workers:
|
||||
@@ -196,54 +248,101 @@ npm run deploy
|
||||
```
|
||||
|
||||
This will:
|
||||
1. Set `ENVIRONMENT=production` to activate code-only mode
|
||||
1. Set `ENVIRONMENT=production` to prevent automatic schema syncing
|
||||
2. Build the Vite application
|
||||
3. Deploy to Cloudflare Workers using Wrangler
|
||||
3. Sync the database schema (safe operations only)
|
||||
4. Deploy to Cloudflare Workers using Wrangler
|
||||
|
||||
In production, bknd will:
|
||||
- Use the configuration from `bknd-config.json` (read-only)
|
||||
- Use the configuration defined in `config.ts`
|
||||
- Skip config validation for better performance
|
||||
- Expect secrets to be provided via environment variables
|
||||
|
||||
## Environment Variables
|
||||
## How Code Mode Works
|
||||
|
||||
Make sure to set your secrets in the Cloudflare Workers dashboard or via Wrangler:
|
||||
1. **Define Schema:** Create entities and fields using the Drizzle-like API in `config.ts`
|
||||
2. **Convert to JSON:** Use `schema.toJSON()` to convert your schema to bknd's configuration format
|
||||
3. **Manual Types:** Optionally declare types inline for IDE support and type safety
|
||||
4. **Deploy:** Same configuration runs in both development and production
|
||||
|
||||
### Code Mode vs Hybrid Mode
|
||||
|
||||
| Feature | Code Mode | Hybrid Mode |
|
||||
|---------|-----------|-------------|
|
||||
| Schema Definition | Code-only (`em`, `entity`, `text`) | Visual UI in dev, code in prod |
|
||||
| Configuration Files | None (all in code) | Auto-generated `bknd-config.json` |
|
||||
| Type Generation | Manual or opt-in | Automatic |
|
||||
| Setup Complexity | Minimal | Requires plugins & filesystem access |
|
||||
| Use Case | Traditional code-first workflows | Rapid prototyping, visual development |
|
||||
|
||||
## Type Generation (Optional)
|
||||
|
||||
This example intentionally **does not use automatic type generation** to simulate a typical development environment where types are managed manually. This approach:
|
||||
- Reduces build complexity
|
||||
- Eliminates dependency on build-time tooling
|
||||
- Works in any environment without special plugins
|
||||
|
||||
However, if you prefer automatic type generation, you can easily add it:
|
||||
|
||||
### Option 1: Using the Vite Plugin and `code` helper presets
|
||||
Add `typesFilePath` to your config:
|
||||
|
||||
```typescript
|
||||
export default code({
|
||||
typesFilePath: "./bknd-types.d.ts",
|
||||
// ... rest of config
|
||||
});
|
||||
```
|
||||
|
||||
For Cloudflare Workers, you'll need the `devFsVitePlugin`:
|
||||
```typescript
|
||||
// vite.config.ts
|
||||
import { devFsVitePlugin } from "bknd/adapter/cloudflare";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
// ...
|
||||
devFsVitePlugin({ configFile: "config.ts" })
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
Finally, add the generated types to your `tsconfig.json`:
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["./bknd-types.d.ts"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This provides filesystem access for auto-syncing types despite Cloudflare's `unenv` restrictions.
|
||||
|
||||
### Option 2: Using the CLI
|
||||
|
||||
You may also use the CLI to generate types:
|
||||
|
||||
```sh
|
||||
# Example: Set JWT secret
|
||||
npx wrangler secret put auth.jwt.secret
|
||||
npx bknd types --outfile ./bknd-types.d.ts
|
||||
```
|
||||
|
||||
Check `.env.example` for all required secrets after running the app in development mode.
|
||||
## Database Seeding
|
||||
|
||||
## How Hybrid Mode Works
|
||||
Unlike UI-only and hybrid modes where bknd can automatically detect an empty database (by attempting to fetch the configuration. A "table not found" error indicates a fresh database), **code mode requires manual seeding**. This is because in code mode, the configuration is always provided from code, so bknd can't determine if the database is empty without additional queries, which would impact performance.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Development] -->|Visual Config| B[Admin UI]
|
||||
B -->|Auto-sync| C[bknd-config.json]
|
||||
B -->|Auto-sync| D[bknd-types.d.ts]
|
||||
C -->|Deploy| E[Production]
|
||||
E -->|Read-only| F[Code-only Mode]
|
||||
This example includes a [`seed.ts`](./seed.ts) file that you can run manually. For Cloudflare, it uses `bknd.config.ts` (with `withPlatformProxy`) to access Cloudflare resources like D1 during CLI execution:
|
||||
|
||||
```sh
|
||||
npm run bknd:seed
|
||||
```
|
||||
|
||||
1. **In Development:** `mode: "db"` - Configuration stored in database, editable via Admin UI
|
||||
2. **Auto-sync:** Changes automatically written to `bknd-config.json` and types to `bknd-types.d.ts`
|
||||
3. **In Production:** `mode: "code"` - Configuration read from `bknd-config.json`, no database overhead
|
||||
The seed script manually checks if the database is empty before inserting data. See the [seed.ts](./seed.ts) file for implementation details.
|
||||
|
||||
## Why devFsVitePlugin?
|
||||
|
||||
Cloudflare's Vite plugin removes Node.js APIs for Workers compatibility. This breaks filesystem operations needed for:
|
||||
- Auto-syncing TypeScript types (`syncTypes` plugin)
|
||||
- Auto-syncing configuration (`syncConfig` plugin)
|
||||
- Auto-syncing secrets (`syncSecrets` plugin)
|
||||
|
||||
The `devFsVitePlugin` + `devFsWrite` combination provides a workaround by using Vite's module system to enable file writes during development.
|
||||
|
||||
## Want to learn more?
|
||||
## Want to Learn More?
|
||||
|
||||
- [Cloudflare Integration Documentation](https://docs.bknd.io/integration/cloudflare)
|
||||
- [Hybrid Mode Guide](https://docs.bknd.io/usage/introduction#hybrid-mode)
|
||||
- [Code Mode Guide](https://docs.bknd.io/usage/introduction#code-only-mode)
|
||||
- [Mode Helpers Documentation](https://docs.bknd.io/usage/introduction#mode-helpers)
|
||||
- [Data Structure & Schema API](https://docs.bknd.io/usage/database#data-structure)
|
||||
- [Discord Community](https://discord.gg/952SFk8Tb8)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user