{"id":46729774,"url":"https://github.com/acrichards3/create-vex-app","last_synced_at":"2026-03-11T12:25:48.871Z","repository":{"id":321888998,"uuid":"1087535801","full_name":"acrichards3/create-vex-app","owner":"acrichards3","description":"Stop wrestling with annoying AI slop. Vex App is a Bun-powered full-stack starter kit with Vite + React, Hono, and a shared TypeScript lib, built with strict guardrails that make AI agents write production-quality code from the first keystroke.","archived":false,"fork":false,"pushed_at":"2026-03-08T05:03:54.000Z","size":1058,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-08T08:21:12.387Z","etag":null,"topics":["bun","cli","create-vex-app","drizzle-orm","honojs","typescript","vex-app","vite"],"latest_commit_sha":null,"homepage":"https://vexapp.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/acrichards3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-01T05:26:38.000Z","updated_at":"2026-03-08T05:43:46.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/acrichards3/create-vex-app","commit_stats":null,"previous_names":["acrichards3/thunder-cli","acrichards3/create-thunder-app","acrichards3/create-vex-app"],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/acrichards3/create-vex-app","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acrichards3%2Fcreate-vex-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acrichards3%2Fcreate-vex-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acrichards3%2Fcreate-vex-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acrichards3%2Fcreate-vex-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/acrichards3","download_url":"https://codeload.github.com/acrichards3/create-vex-app/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acrichards3%2Fcreate-vex-app/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30286041,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T02:57:19.223Z","status":"ssl_error","status_checked_at":"2026-03-09T02:56:26.373Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bun","cli","create-vex-app","drizzle-orm","honojs","typescript","vex-app","vite"],"created_at":"2026-03-09T15:00:21.839Z","updated_at":"2026-03-09T15:00:41.922Z","avatar_url":"https://github.com/acrichards3.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://www.vexapp.io/logos/vex-app-logo.png\" alt=\"Vex App Logo\" width=\"200\" /\u003e\n\u003c/div\u003e\n\n# Vex App Template\n\n[![npm version](https://img.shields.io/npm/v/create-vex-app)](https://www.npmjs.com/package/create-vex-app)\n[![CI](https://github.com/acrichards3/create-vex-app/actions/workflows/ci.yml/badge.svg)](https://github.com/acrichards3/create-vex-app/actions/workflows/ci.yml)\n[![license](https://img.shields.io/npm/l/create-vex-app)](./LICENSE)\n\nStop wrestling with annoying AI slop. Vex App is a Bun-powered full-stack starter kit with Vite + React, Hono, and a shared TypeScript lib, built with strict guardrails that make AI agents write production-quality code from the first keystroke.\n\n**[Documentation](https://www.vexapp.io)**\n\n## 🚀 Quickstart\n\nCreate a new app in the current directory (you'll be prompted for a name):\n\n```bash\nbun create vex-app@latest\n```\n\nOr specify the name directly:\n\n```bash\nbun create vex-app@latest my-app\n```\n\nThe generator will:\n\n- Copy the template into `./my-app` (or the name you provide)\n- Rename all package scopes to `@\u003cyour-app\u003e/...`\n- Seed env files with sensible defaults\n- Optionally include GitHub CI/CD pipeline\n- Optionally configure AI settings (strict ESLint, Cursor rules, post-write hooks)\n- Optionally enable the spec-first workflow (AI writes test specs before implementing)\n- Ask to run `bun install` (workspaces) and optionally build `lib`\n\n## 📁 Project Structure\n\n```\nmy-app/\n├── frontend/               # React + Vite application\n│   ├── src/\n│   │   ├── routes/          # File-based routes (TanStack Router)\n│   │   │   ├── __root.tsx   # Root layout (providers, shared UI)\n│   │   │   └── index.tsx    # Home page (/)\n│   │   ├── api/             # API call utilities\n│   │   ├── components/      # Reusable UI components\n│   │   └── env/             # Environment variable validation\n│   │       ├── schema.ts    # Zod schema (edit this to add vars)\n│   │       ├── validate.ts  # Validation logic\n│   │       └── env.ts       # Typed env object\n│   └── vite.config.ts       # Vite config (plugins, proxy, aliases)\n├── backend/                 # Hono API server\n│   ├── src/\n│   │   ├── index.ts         # Server entry (CORS, CSRF, auth, routes)\n│   │   ├── db/schema/       # Drizzle ORM table definitions\n│   │   ├── env/             # Environment variable validation\n│   │   └── security/        # Rate limiting, token encryption, hashing\n│   └── drizzle.config.ts    # Drizzle Kit config\n├── lib/                     # Shared TypeScript utilities and types\n│   └── src/\n│       ├── utils/           # assertNever, raise, tryCatch, objectUtils\n│       └── types/           # Shared types (User, etc.)\n├── scripts/                 # Dev script (parallel service runner)\n├── .cursor/rules/           # Cursor AI rules for project conventions\n├── .cursor/hooks/           # Post-write hooks (ESLint, Prettier, tsc, jscpd)\n├── .jscpd.json              # Duplicate code detection config\n├── .github/workflows/       # CI/CD pipeline\n├── package.json             # Root workspace config \u0026 scripts\n├── tsconfig.json            # TypeScript project references\n├── .prettierrc              # Prettier config\n└── .prettierignore          # Files excluded from Prettier\n```\n\n## 💻 Development Commands (run from repo root)\n\n| Command                  | Description                                      |\n| ------------------------ | ------------------------------------------------ |\n| `bun run dev`            | Start frontend, lib, and backend                 |\n| `bun run dev:frontend`   | Start only the frontend (Vite, port 5173)        |\n| `bun run dev:backend`    | Start only the backend (Hono, port 3000)         |\n| `bun run dev:lib`        | Watch mode for lib package (rebuilds on changes) |\n| `bun run build`          | Build all packages (includes typecheck \u0026 lint)   |\n| `bun run build:all`      | Build all packages without typecheck/lint        |\n| `bun run build:frontend` | Build only the frontend package                  |\n| `bun run build:backend`  | Build only the backend package                   |\n| `bun run build:lib`      | Build only the lib package                       |\n| `bun run typecheck`      | Type check all packages                          |\n| `bun run test`           | Run all tests across all packages                |\n| `bun run test:frontend`  | Run tests in the frontend package only           |\n| `bun run test:backend`   | Run tests in the backend package only            |\n| `bun run test:lib`       | Run tests in the lib package only                |\n| `bun run lint`           | Lint all packages (format check + ESLint)        |\n| `bun run lint:fix`       | Auto-fix linting issues                          |\n| `bun run format`         | Format all files with Prettier                   |\n| `bun run format:check`   | Check formatting without fixing                  |\n\n### Database Commands\n\n| Command               | Description                     |\n| --------------------- | ------------------------------- |\n| `bun run db:generate` | Generate database migrations    |\n| `bun run db:migrate`  | Run database migrations         |\n| `bun run db:push`     | Push schema changes to database |\n| `bun run db:studio`   | Open Drizzle Studio             |\n\n## 📦 Using the Shared Library Package\n\nThe `lib` package contains shared TypeScript utilities that can be imported in both the frontend and backend:\n\n```typescript\nimport { raise, tryCatch, tryCatchAsync, assertNever } from \"@your-project/lib\";\nimport type { User } from \"@your-project/lib\";\n```\n\n**Important:** If you make changes to the `lib` package, you must rebuild it:\n\n```bash\nbun run build:lib\n```\n\nOr run it in watch mode during development (already included in `bun run dev`):\n\n```bash\nbun run dev:lib\n```\n\n## 🗄️ Database Setup\n\nThis template uses **Drizzle ORM** with **PostgreSQL**. The database schema is defined in `backend/src/db/schema/`.\n\n### Setup Steps\n\n1. **Set up your database** (local PostgreSQL or cloud provider)\n\n2. **Configure environment variables** in `backend/.env` (see [Environment Variables](#-environment-variables) section)\n\n3. **Create your schema** in `backend/src/db/schema/` (e.g., `users.ts`, `posts.ts`)\n\n4. **Push schema to database:**\n\n   ```bash\n   bun run db:push\n   ```\n\n   Or generate and run migrations:\n\n   ```bash\n   bun run db:generate\n   bun run db:migrate\n   ```\n\n5. **Open Drizzle Studio** to view/edit data:\n\n   ```bash\n   bun run db:studio\n   ```\n\n## 🔐 Authentication Setup\n\nThis template includes **Auth.js** (formerly NextAuth) integration with **Google OAuth** as the default provider. Authentication uses database sessions for secure, server-side session management.\n\n### Backend Setup\n\n1. **Set up Google OAuth credentials:**\n   - Go to [Google Cloud Console](https://console.cloud.google.com/apis/credentials)\n   - Create a new OAuth 2.0 Client ID (Web application)\n   - Add authorized redirect URI: `http://localhost:3000/api/auth/callback/google`\n   - Copy the Client ID and Client Secret\n\n2. **Configure backend environment variables** in `backend/.env`:\n\n   ```bash\n   AUTH_SECRET=your-auth-secret-here  # Generate with: openssl rand -base64 32\n   DATABASE_URL=postgresql://user:password@localhost:5432/dbname\n   FRONTEND_URL=http://localhost:5173\n   GOOGLE_CLIENT_ID=your-google-client-id\n   GOOGLE_CLIENT_SECRET=your-google-client-secret\n   PORT=3000\n   ```\n\n3. **Push the auth schema to your database:**\n\n   ```bash\n   bun run db:push\n   ```\n\n### Frontend Setup\n\n1. **Configure frontend environment variables** in `frontend/.env`:\n\n   ```bash\n   VITE_BACKEND_URL=http://localhost:3000\n   VITE_PORT=5173\n   ```\n\n2. **The frontend is already configured** with `SessionProvider` (in `__root.tsx`) and auth hooks. The sign-in button and session management are ready to use.\n\n3. **Vite proxy is configured** — Auth API requests (`/api/auth/*`) are automatically proxied to the backend via `vite.config.ts`. This allows the frontend to use relative URLs for auth endpoints.\n\n### Adding More Providers\n\nTo add additional OAuth providers (GitHub, Discord, etc.):\n\n1. **Add provider to backend** in `backend/src/index.ts`:\n\n   ```typescript\n   import GitHub from \"@auth/core/providers/github\";\n\n   providers: [\n     Google({ clientId: env.GOOGLE_CLIENT_ID, clientSecret: env.GOOGLE_CLIENT_SECRET }),\n     GitHub({ clientId: env.GITHUB_CLIENT_ID, clientSecret: env.GITHUB_CLIENT_SECRET }),\n   ],\n   ```\n\n2. **Add environment variables** to `backend/src/env/env.ts` (schema) and `backend/.env` (values)\n\n3. **Update the frontend sign-in button:**\n\n   ```typescript\n   \u003cbutton onClick={() =\u003e signIn(\"github\")}\u003eSign in with GitHub\u003c/button\u003e\n   ```\n\n## 🛠️ Tech Stack\n\n### Frontend\n\n- **React 19** - UI library\n- **Vite 5** - Build tool and dev server\n- **TypeScript 5.9** - Type safety\n- **TanStack Router** - File-based routing with automatic code splitting\n- **TanStack Query (React Query)** - Data fetching and caching\n- **Tailwind CSS v4** - Styling\n- **Zod v4** - Runtime type validation\n- **@hono/auth-js/react** - Auth.js React client hooks\n\n### Backend\n\n- **Hono v4** - Fast web framework\n- **TypeScript 5.9** - Type safety\n- **Drizzle ORM** - Type-safe SQL ORM\n- **PostgreSQL** - Database (via Bun's built-in SQL driver)\n- **Zod v4** - Runtime type validation\n- **Auth.js (@hono/auth-js)** - Authentication framework\n- **@auth/drizzle-adapter** - Drizzle adapter for Auth.js\n\n### Shared\n\n- **Bun** - Runtime and package manager\n- **ESLint** - Code linting\n- **Prettier** - Code formatting\n\n## 🔒 Security Defaults\n\nOut of the box, the backend enables several defenses:\n\n- Secure headers via `hono/secure-headers`\n- CSRF protection via `hono/csrf`; the frontend sends `X-CSRF-Token` on mutating requests\n- CORS restricted to `FRONTEND_URL` with credentials and `X-CSRF-Token` allowed\n- Rate limiting on `/api/auth/*` (10 requests / 60s per IP+path) to mitigate brute force and callback abuse\n- Sessions and verification tokens are stored as SHA-256 hashes (no plaintext)\n- OAuth tokens (access/refresh/id) can be encrypted at rest (AES-256-GCM) when you set `OAUTH_TOKEN_ENCRYPTION_KEY`\n\nRecommended hardening for production (left to end users):\n\n- Add a Content Security Policy (CSP) with nonces for scripts\n- Consider an Origin/Referer check for POST/PUT/PATCH/DELETE as defense-in-depth\n- Ensure cookies are `Secure`, `HttpOnly`, and `SameSite=Lax/Strict` behind HTTPS\n- If deploying multiple instances, replace in-memory rate limiting with a shared store (e.g., Redis)\n\n## 🔧 Environment Variables\n\n### Backend (`backend/.env`)\n\n```bash\n# Authentication\nAUTH_SECRET=               # Generate with: openssl rand -base64 32\n\n# Database\nDATABASE_URL=postgresql://user:password@localhost:5432/dbname\n\n# Environment\nENVIRONMENT=development    # development | production | testing\n\n# Frontend URL (for CORS and auth redirects)\nFRONTEND_URL=http://localhost:5173\n\n# Google OAuth (required)\nGOOGLE_CLIENT_ID=\nGOOGLE_CLIENT_SECRET=\n\n# Token encryption (optional — encrypts OAuth tokens at rest)\nOAUTH_TOKEN_ENCRYPTION_KEY=   # Generate with: openssl rand -base64 32\n\n# Server\nPORT=3000\n```\n\n**Required variables:**\n\n- `AUTH_SECRET` - Secret for signing sessions (generate with `openssl rand -base64 32`)\n- `DATABASE_URL` - PostgreSQL connection string\n- `GOOGLE_CLIENT_ID` - Google OAuth client ID\n- `GOOGLE_CLIENT_SECRET` - Google OAuth client secret\n\n**Optional variables:**\n\n- `ENVIRONMENT` - Runtime environment (default: `development`)\n- `FRONTEND_URL` - Frontend URL for redirects (default: `http://localhost:5173`)\n- `OAUTH_TOKEN_ENCRYPTION_KEY` - Encrypts OAuth tokens at rest with AES-256-GCM\n- `PORT` - Backend server port (default: `3000`)\n\n### Frontend (`frontend/.env`)\n\n```bash\nVITE_BACKEND_URL=http://localhost:3000\nVITE_PORT=5173\n```\n\n- `VITE_BACKEND_URL` - Backend API URL (**required**)\n- `VITE_PORT` - Frontend dev server port (default: `5173`)\n\nEnvironment variables are validated at startup with Zod. The backend logs errors and exits; the frontend renders a friendly error page in the browser. Edit `frontend/src/env/schema.ts` to add new frontend variables.\n\n**Note:** All `VITE_` variables are embedded in the client bundle and publicly visible. Never put secrets in frontend env vars. Restart the dev server after changing `.env` files.\n\n## 📝 Code Quality\n\n- **TypeScript** - Strict mode enabled across all packages\n- **ESLint** - Type-aware linting across workspaces (TS project aware):\n  - Prefer `??` over `||` for defaulting\n  - Flag impossible conditions (`@typescript-eslint/no-unnecessary-condition`)\n  - Enforce `import type` when symbols are used as types only\n  - Alphabetical key sorting for cleaner diffs\n- **Prettier** - Automatic code formatting (configured via `.prettierrc`)\n- **Type checking** - Run `bun run typecheck` to verify all packages\n\n## 🤖 AI Integration\n\nWhen you select \"Use Vex App recommended AI settings\" during setup, the CLI configures your project for AI-assisted development:\n\n- **Strict ESLint config** — Swaps in a hardened ruleset with `sonarjs`, `unicorn`, and `perfectionist` plugins. Enforces explicit return types, bans type assertions, prevents mutation, bans raw `try/catch` blocks (use `tryCatch`/`tryCatchAsync` utilities instead), limits complexity, and more. Designed to catch the mistakes AI models make most often.\n- **Cursor rules** (`.cursor/rules/`) — `.mdc` files with frontmatter that Cursor automatically injects into the AI model's context. Cover component organization, Tailwind conventions, type safety patterns, Zod v4 usage, Bun APIs, backend architecture, and testing conventions.\n- **Spec-first workflow** (optional) — When enabled, the AI writes empty test specs (WHEN/AND/it decision trees) for every layer before writing any implementation code, then stops and asks you to approve the paths before building.\n- **Post-write hooks** (`.cursor/hooks/`) — Four shell scripts that run automatically after every AI file write:\n  1. **Prettier** — Auto-formats the file\n  2. **ESLint** — Auto-fixes what it can, blocks the write if errors remain\n  3. **TypeScript** — Runs `tsc --noEmit`, blocks on type errors\n  4. **jscpd** — Detects duplicate code, blocks if clones are found\n- **Duplicate detection** (`.jscpd.json`) — Configures thresholds for copy-paste detection\n\nIf you opt out of AI settings, you get the standard ESLint config without extra plugins and no `.cursor/` directory.\n\n\u003e **Note:** The hooks require `jq` to be installed on your system. Most macOS and Linux systems have it pre-installed.\n\n## 🔧 Publishing This Template (For Maintainers)\n\nTo publish this template to npm so others can use it:\n\n1. **Make sure you're logged into npm:**\n\n   ```bash\n   npm login\n   ```\n\n2. **Bump the version and publish to npm:**\n\n   ```bash\n   npm version patch   # or minor/major as appropriate\n   npm publish --access public\n   git push --follow-tags\n   ```\n\n3. **Users can then create projects with:**\n   ```bash\n   bun create vex-app@latest\n   ```\n\nNote: The package name is `create-vex-app`, but users call it with `bun create vex-app` (Bun automatically prepends `create-`).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facrichards3%2Fcreate-vex-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Facrichards3%2Fcreate-vex-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facrichards3%2Fcreate-vex-app/lists"}