| 1 | # bhvr đĻĢ |
| 2 | |
| 3 |  |
| 4 | |
| 5 | A full-stack TypeScript monorepo starter with shared types, using Bun, Hono, Vite, and React |
| 6 | |
| 7 | ## Why bhvr? |
| 8 | |
| 9 | While there are plenty of existing app building stacks out there, many of them are either bloated, outdated, or have too much of a vendor lock-in. bhvr is built with the opinion that you should be able to deploy your client or server in any environment while also keeping type saftey. |
| 10 | |
| 11 | ## Features |
| 12 | |
| 13 | - **Full-Stack TypeScript**: End-to-end type safety between client and server |
| 14 | - **Shared Types**: Common type definitions shared between client and server |
| 15 | - **Monorepo Structure**: Organized as a workspaces-based monorepo |
| 16 | - **Modern Stack**: |
| 17 | - [Bun](https://bun.sh) as the JavaScript runtime |
| 18 | - [Hono](https://hono.dev) as the backend framework |
| 19 | - [Vite](https://vitejs.dev) for frontend bundling |
| 20 | - [React](https://react.dev) for the frontend UI |
| 21 | |
| 22 | ## Project Structure |
| 23 | |
| 24 | ``` |
| 25 | . |
| 26 | âââ client/ # React frontend |
| 27 | âââ server/ # Hono backend |
| 28 | âââ shared/ # Shared TypeScript definitions |
| 29 | â âââ src/types/ # Type definitions used by both client and server |
| 30 | âââ package.json # Root package.json with workspaces |
| 31 | ``` |
| 32 | |
| 33 | ### Server |
| 34 | |
| 35 | bhvr uses Hono as a backend API for it's simplicity and massive ecosystem of plugins. If you have ever used Express then it might feel familiar. Declaring routes and returning data is easy. |
| 36 | |
| 37 | ``` |
| 38 | server |
| 39 | âââ bun.lock |
| 40 | âââ package.json |
| 41 | âââ README.md |
| 42 | âââ src |
| 43 | â  âââ index.ts |
| 44 | âââ tsconfig.json |
| 45 | ``` |
| 46 | |
| 47 | ```typescript src/index.ts |
| 48 | import { Hono } from 'hono' |
| 49 | import { cors } from 'hono/cors' |
| 50 | import type { ApiResponse } from 'shared/dist' |
| 51 | |
| 52 | const app = new Hono() |
| 53 | |
| 54 | app.use(cors()) |
| 55 | |
| 56 | app.get('/', (c) => { |
| 57 | return c.text('Hello Hono!') |
| 58 | }) |
| 59 | |
| 60 | app.get('/hello', async (c) => { |
| 61 | |
| 62 | const data: ApiResponse = { |
| 63 | message: "Hello BHVR!", |
| 64 | success: true |
| 65 | } |
| 66 | |
| 67 | return c.json(data, { status: 200 }) |
| 68 | }) |
| 69 | |
| 70 | export default app |
| 71 | ``` |
| 72 | |
| 73 | If you wanted to add a database to Hono you can do so with a multitude of Typescript libraries like [Supabase](https://supabase.com), or ORMs like [Drizzle](https://orm.drizzle.team/docs/get-started) or [Prisma](https://www.prisma.io/orm) |
| 74 | |
| 75 | ### Client |
| 76 | |
| 77 | bhvr uses Vite + React Typescript template, which means you can build your frontend just as you would with any other React app. This makes it flexible to add UI components like [shadcn/ui](https://ui.shadcn.com) or routing using [React Router](https://reactrouter.com/start/declarative/installation). |
| 78 | |
| 79 | ``` |
| 80 | client |
| 81 | âââ eslint.config.js |
| 82 | âââ index.html |
| 83 | âââ package.json |
| 84 | âââ public |
| 85 | â  âââ vite.svg |
| 86 | âââ README.md |
| 87 | âââ src |
| 88 | â  âââ App.css |
| 89 | â  âââ App.tsx |
| 90 | â  âââ assets |
| 91 | â  âââ index.css |
| 92 | â  âââ main.tsx |
| 93 | â  âââ vite-env.d.ts |
| 94 | âââ tsconfig.app.json |
| 95 | âââ tsconfig.json |
| 96 | âââ tsconfig.node.json |
| 97 | âââ vite.config.ts |
| 98 | ``` |
| 99 | |
| 100 | ```typescript src/App.tsx |
| 101 | import { useState } from 'react' |
| 102 | import beaver from './assets/beaver.svg' |
| 103 | import { ApiResponse } from 'shared' |
| 104 | import './App.css' |
| 105 | |
| 106 | const SERVER_URL = import.meta.env.VITE_SERVER_URL || "http://localhost:3000" |
| 107 | |
| 108 | function App() { |
| 109 | const [data, setData] = useState<ApiResponse | undefined>() |
| 110 | |
| 111 | async function sendRequest() { |
| 112 | try { |
| 113 | const req = await fetch(`${SERVER_URL}/hello`) |
| 114 | const res: ApiResponse = await req.json() |
| 115 | setData(res) |
| 116 | } catch (error) { |
| 117 | console.log(error) |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | return ( |
| 122 | <> |
| 123 | <div> |
| 124 | <a href="https://github.com/stevedylandev/bhvr" target="_blank"> |
| 125 | <img src={beaver} className="logo" alt="beaver logo" /> |
| 126 | </a> |
| 127 | </div> |
| 128 | <h1>bhvr</h1> |
| 129 | <h2>Bun + Hono + Vite + React</h2> |
| 130 | <p>A typesafe fullstack monorepo</p> |
| 131 | <div className="card"> |
| 132 | <button onClick={sendRequest}> |
| 133 | Call API |
| 134 | </button> |
| 135 | {data && ( |
| 136 | <pre className='response'> |
| 137 | <code> |
| 138 | Message: {data.message} <br /> |
| 139 | Success: {data.success.toString()} |
| 140 | </code> |
| 141 | </pre> |
| 142 | )} |
| 143 | </div> |
| 144 | <p className="read-the-docs"> |
| 145 | Click the beaver to learn more |
| 146 | </p> |
| 147 | </> |
| 148 | ) |
| 149 | } |
| 150 | |
| 151 | export default App |
| 152 | ``` |
| 153 | |
| 154 | ### Shared |
| 155 | |
| 156 | The Shared package is used for anything you want to share between the Server and Client. This could be types or libraries that you use in both the enviorments. |
| 157 | |
| 158 | ``` |
| 159 | shared |
| 160 | âââ package.json |
| 161 | âââ src |
| 162 | â  âââ index.ts |
| 163 | â  âââ types |
| 164 | â  âââ index.ts |
| 165 | âââ tsconfig.json |
| 166 | ``` |
| 167 | |
| 168 | Inside the `src/index.ts` we export any of our code from the folders so it's usabe in other parts of the monorepo |
| 169 | |
| 170 | ```typescript |
| 171 | export * from "./types" |
| 172 | ``` |
| 173 | |
| 174 | By running `bun run dev` or `bun run build` it will compile and export the packages from `shared` so it can be used in either `client` or `server` |
| 175 | |
| 176 | ```typescript |
| 177 | import { ApiResponse } from 'shared' |
| 178 | ``` |
| 179 | |
| 180 | ## Getting Started |
| 181 | |
| 182 | ### Quick Start |
| 183 | |
| 184 | You can start a new bhvr project using the [CLI](https://github.com/stevedylandev/create-bhvr) |
| 185 | |
| 186 | ```bash |
| 187 | bun create bhvr |
| 188 | ``` |
| 189 | |
| 190 | ### Installation |
| 191 | |
| 192 | ```bash |
| 193 | # Install dependencies for all workspaces |
| 194 | bun install |
| 195 | ``` |
| 196 | |
| 197 | ### Development |
| 198 | |
| 199 | ```bash |
| 200 | # Run shared types in watch mode, server, and client all at once |
| 201 | bun run dev |
| 202 | |
| 203 | # Or run individual parts |
| 204 | bun run dev:shared # Watch and compile shared types |
| 205 | bun run dev:server # Run the Hono backend |
| 206 | bun run dev:client # Run the Vite dev server for React |
| 207 | ``` |
| 208 | |
| 209 | ### Building |
| 210 | |
| 211 | ```bash |
| 212 | # Build everything |
| 213 | bun run build |
| 214 | |
| 215 | # Or build individual parts |
| 216 | bun run build:shared # Build the shared types package |
| 217 | bun run build:client # Build the React frontend |
| 218 | ``` |
| 219 | |
| 220 | ### Deployment |
| 221 | |
| 222 | Deplying each piece is very versatile and can be done numerous ways, and exploration into automating these will happen at a later date. Here are some references in the meantime. |
| 223 | |
| 224 | **Client** |
| 225 | - [Orbiter](https://orbiter.host) |
| 226 | - [GitHub Pages](https://vite.dev/guide/static-deploy.html#github-pages) |
| 227 | - [Netlify](https://vite.dev/guide/static-deploy.html#netlify) |
| 228 | - [Cloudflare Pages](https://vite.dev/guide/static-deploy.html#cloudflare-pages) |
| 229 | |
| 230 | **Server** |
| 231 | - [Cloudflare Worker](https://gist.github.com/stevedylandev/4aa1fc569bcba46b7169193c0498d0b3) |
| 232 | - [Bun](https://hono.dev/docs/getting-started/bun) |
| 233 | - [Node.js](https://hono.dev/docs/getting-started/nodejs) |
| 234 | |
| 235 | ## Type Sharing |
| 236 | |
| 237 | Types are automatically shared between the client and server thanks to the shared package and TypeScript path aliases. You can import them in your code using: |
| 238 | |
| 239 | ```typescript |
| 240 | import { ApiResponse } from '@shared/types'; |
| 241 | ``` |
| 242 | |
| 243 | ## Learn More |
| 244 | |
| 245 | - [Bun Documentation](https://bun.sh/docs) |
| 246 | - [Vite Documentation](https://vitejs.dev/guide/) |
| 247 | - [React Documentation](https://react.dev/learn) |
| 248 | - [Hono Documentation](https://hono.dev/docs) |
| 249 | - [TypeScript Documentation](https://www.typescriptlang.org/docs/) |