Ship
Configuration
A Goribu app has four config files, and create-goribu ships working defaults for all of them: goribu.config.js (databases and app settings), wrangler.jsonc (the Cloudflare runtime), vite.config.js (the build pipeline) and .env / .env.production (config and secrets). In practice most apps only ever touch goribu.config.js and the .env files.
goribu.config.js
Both goribu dev and goribu deploy read this file. The minimum is an empty object but a typical app for now just declares its database. For D1 you write a human-readable name and nothing else:
export default {
database: { type: "d1", name: "my-app-db" },
};For Postgres you reference the two env values (see Postgres for the full story):
export default {
database: {
type: "postgres",
url: process.env.POSTGRES_URL,
hyperdriveId: process.env.HYPERDRIVE_ID,
},
};.env is loaded before the config is evaluated, so process.env.* references resolve. Do not add d1_databases or hyperdrive entries to wrangler.jsonc as Goribu synthesizes those bindings from here.
Environment variables
.env holds local values, .env.production holds production values. Same keys, different values per file. Read server-side values from req.env and never process.env directly in app code.
export async function POST(req, res) {
const key = req.env.STRIPE_KEY; // server-only
}A variable is exposed to the browser only if its name starts with PUBLIC_, read in client code as import.meta.env.PUBLIC_*. Everything else stays server-side.
function Header() {
return <a href={import.meta.env.PUBLIC_APP_URL}>Home</a>;
}Keep a committed .env.example documenting which keys exist; the scaffold gitignores .env and .env.* but allows !.env.example.
Going deeper
Full goribu.config.js options
| Key | When | Notes |
|---|---|---|
route |
optional | A Cloudflare route like "app.example.com/*". Omit it to deploy to the *.workers.dev URL, or when attaching a Custom Domain from the dashboard. See Deployment. |
database.type |
with a database | "d1" or "postgres". |
database.name |
D1, required | Human-readable name; keys a local SQLite file in dev, the Cloudflare resource in prod. |
database.url |
Postgres | Connection string, usually process.env.POSTGRES_URL. The deploy-time value is the migration target and needs DDL permission. |
database.hyperdriveId |
Postgres, prod | Hyperdrive resource ID from the dashboard, usually process.env.HYPERDRIVE_ID. |
The file is loaded as ESM (goribu.config.js or .mjs) and must default-export an object. The legacy bindings key is rejected with a migration message.
wrangler.jsonc
The runtime config Cloudflare actually reads. Bindings, compatibility date, the Static Assets directory. The scaffold ships:
{
"name": "my-app",
"main": "src/server.jsx",
"compatibility_date": "2026-03-01",
"compatibility_flags": ["nodejs_compat"],
"assets": { "directory": "./dist/client", "binding": "ASSETS" }
}main is your Worker entry (the file that calls createApp). nodejs_compat is required as Goribu's runtime imports node: modules. assets.directory must match what the build writes. Goribu merges its synthesized database bindings into .goribu/wrangler.dev.jsonc and .goribu/wrangler.deploy.jsonc, so leave d1_databases/hyperdrive out of this file. Use it for everything Goribu hasn't abstracted yet like KV, R2, queues, Durable Objects, service bindings, which then appear on req.env under their binding name.
vite.config.js
Four plugins, in a fixed order but remember to keep goribu first because it generates the route manifest the rest depend on, cloudflare last because it wraps the SSR environment:
import { defineConfig } from "vite";
import goribu from "goribu/vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { cloudflare } from "@cloudflare/vite-plugin";
export default defineConfig({
plugins: [goribu(), react(), tailwindcss(), cloudflare()],
});The goribu() plugin takes a few options for non-standard layouts: routesDir (default "src/routes"), documentPath (default "src/document.jsx", scanned for <Stylesheet> references), stylesheets (extra paths the scanner can't see) and manifestPath/clientManifestPath (where the generated manifests are written, defaulting under .goribu/).
Path aliases
create-goribu aliases @/ to src/. Two files must agree, or one of build and editor autocomplete breaks: declare it in vite.config.js (resolve.alias) for the build and in jsconfig.json/tsconfig.json (compilerOptions.paths) for the editor.
How secrets reach production
goribu deploy invokes wrangler deploy --secrets-file=.env.production when that file exists, so its values become Cloudflare secrets in the same operation that ships the Worker. Secrets not in the file are preserved from the previous deploy; if the file is absent, the deploy uses whatever secrets are already configured. For TypeScript apps, npx wrangler types generates Cloudflare binding types. See TypeScript for typing req.env.