Skip to content

Commit 321eb1c

Browse files
committed
chore: initial commit after project scaffolding
0 parents  commit 321eb1c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3525
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/node_modules/
2+
.turbo

.husky/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lint-staged

README.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# React + Tanstack Router + Hono + Drizzle
2+
3+
This project was created with a modern TypeScript stack that combines React, TanStack Router, Hono, tRPC, and more.
4+
5+
## Features
6+
7+
- **TypeScript** - For type safety and improved developer experience
8+
- **TanStack Router** - File-based routing with full type safety
9+
- **TailwindCSS** - Utility-first CSS for rapid UI development
10+
- **shadcn/ui** - Reusable UI components
11+
- **Hono** - Lightweight, performant server framework
12+
- **tRPC** - End-to-end type-safe APIs
13+
- **Bun** - Runtime environment
14+
- **Drizzle** - TypeScript-first ORM
15+
- **SQLite/Turso** - Database engine
16+
- **Authentication** - Email & password authentication with Better Auth
17+
- **Biome** - Linting and formatting
18+
- **Husky** - Git hooks for code quality
19+
20+
## Getting Started
21+
22+
First, install the dependencies:
23+
24+
```bash
25+
bun install
26+
```
27+
28+
## Database Setup
29+
30+
This project uses SQLite with Drizzle ORM.
31+
32+
1. Start the local SQLite database:
33+
34+
```bash
35+
cd apps/server && bun db:local
36+
```
37+
38+
2. Update your `.env` file in the `apps/server` directory with the appropriate connection details if needed.
39+
40+
3. Apply the schema to your database:
41+
42+
```bash
43+
bun db:push
44+
```
45+
46+
Then, run the development server:
47+
48+
```bash
49+
bun dev
50+
```
51+
52+
Open [http://localhost:3001](http://localhost:3001) in your browser to see the web application.
53+
54+
The API is running at [http://localhost:3000](http://localhost:3000).
55+
56+
## Environment Variables
57+
58+
Create a `.env` file in both **apps/server** and **apps/web** before starting the app. Below are working examples for local development.
59+
60+
### Backend (`apps/server/.env`)
61+
62+
```env
63+
CORS_ORIGIN=http://localhost:3001
64+
# Generate a fresh 32‑byte secret for production (see next section)
65+
BETTER_AUTH_SECRET=kgQjeoklizgXI6FdfANg9VNse0m0ma9E
66+
BETTER_AUTH_URL=http://localhost:3000
67+
DATABASE_URL=file:./local.db
68+
```
69+
70+
### Frontend (`apps/web/.env`)
71+
72+
```env
73+
VITE_SERVER_URL=http://localhost:3000
74+
```
75+
76+
### Generating a secure `BETTER_AUTH_SECRET`
77+
78+
Generate a cryptographically‑secure secret with **OpenSSL**:
79+
80+
```bash
81+
# 32‑byte base64 string (recommended length for production)
82+
openssl rand -base64 32
83+
```
84+
85+
Copy the output and paste it into the `BETTER_AUTH_SECRET` entry in your backend `.env` file.
86+
87+
> **Tip:** Regenerate the secret when deploying to a different environment (staging, production, etc.) to isolate sessions across environments.
88+
89+
## Project Structure
90+
91+
```
92+
react-tanstack-router-hono-drizzle/
93+
├── apps/
94+
│ ├── web/ # Frontend application (React + TanStack Router)
95+
│ └── server/ # Backend API (Hono, tRPC)
96+
```
97+
98+
## Available Scripts
99+
100+
- `bun dev`: Start all applications in development mode
101+
- `bun build`: Build all applications
102+
- `bun dev:web`: Start only the web application
103+
- `bun dev:server`: Start only the server
104+
- `bun check-types`: Check TypeScript types across all apps
105+
- `bun db:push`: Push schema changes to database
106+
- `bun db:studio`: Open database studio UI
107+
- `cd apps/server && bun db:local`: Start the local SQLite database
108+
- `bun check`: Run Biome formatting and linting

apps/server/.gitignore

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# prod
2+
dist/
3+
/build
4+
/out/
5+
6+
# dev
7+
.yarn/
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/versions
12+
.vscode/*
13+
!.vscode/launch.json
14+
!.vscode/*.code-snippets
15+
.idea/workspace.xml
16+
.idea/usage.statistics.xml
17+
.idea/shelf
18+
.wrangler
19+
/.next/
20+
.vercel
21+
22+
# deps
23+
node_modules/
24+
/node_modules
25+
/.pnp
26+
.pnp.*
27+
28+
# env
29+
.env*
30+
.env.production
31+
.dev.vars
32+
33+
# logs
34+
logs/
35+
*.log
36+
npm-debug.log*
37+
yarn-debug.log*
38+
yarn-error.log*
39+
pnpm-debug.log*
40+
lerna-debug.log*
41+
42+
# misc
43+
.DS_Store
44+
*.pem
45+
46+
# local db
47+
*.db*
48+
49+
# typescript
50+
*.tsbuildinfo
51+
next-env.d.ts

apps/server/drizzle.config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { defineConfig } from "drizzle-kit";
2+
3+
export default defineConfig({
4+
schema: "./src/db/schema",
5+
out: "./src/db/migrations",
6+
dialect: "turso",
7+
dbCredentials: {
8+
url: process.env.DATABASE_URL || "",
9+
authToken: process.env.DATABASE_AUTH_TOKEN,
10+
},
11+
});

apps/server/package.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "server",
3+
"main": "src/index.ts",
4+
"type": "module",
5+
"scripts": {
6+
"build": "tsc && tsc-alias",
7+
"check-types": "tsc --noEmit",
8+
"compile": "bun build --compile --minify --sourcemap --bytecode ./src/index.ts --outfile server",
9+
"dev": "bun run --hot src/index.ts",
10+
"start": "bun run dist/src/index.js",
11+
"db:local": "turso dev --db-file local.db",
12+
"db:push": "drizzle-kit push",
13+
"db:studio": "drizzle-kit studio"
14+
},
15+
"dependencies": {
16+
"dotenv": "^16.4.7",
17+
"zod": "^3.24.2",
18+
"hono": "^4.7.6",
19+
"@hono/trpc-server": "^0.3.4",
20+
"drizzle-orm": "^0.38.4",
21+
"@libsql/client": "^0.14.0",
22+
"better-auth": "^1.2.6",
23+
"@trpc/server": "^11.0.0",
24+
"@trpc/client": "^11.0.0"
25+
},
26+
"devDependencies": {
27+
"tsc-alias": "^1.8.11",
28+
"typescript": "^5.8.2",
29+
"@types/bun": "^1.2.6",
30+
"drizzle-kit": "^0.30.5"
31+
}
32+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { auth } from "@/lib/auth";
2+
import { toNextJsHandler } from "better-auth/next-js";
3+
4+
export const { GET, POST } = toNextJsHandler(auth.handler);

apps/server/src/db/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { createClient } from "@libsql/client";
2+
import { drizzle } from "drizzle-orm/libsql";
3+
4+
const client = createClient({
5+
url: process.env.DATABASE_URL || "",
6+
authToken: process.env.DATABASE_AUTH_TOKEN,
7+
});
8+
9+
export const db = drizzle({ client });

apps/server/src/db/schema/auth.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
2+
3+
export const user = sqliteTable("user", {
4+
id: text("id").primaryKey(),
5+
name: text("name").notNull(),
6+
email: text("email").notNull().unique(),
7+
emailVerified: integer("email_verified", { mode: "boolean" }).notNull(),
8+
image: text("image"),
9+
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
10+
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
11+
});
12+
13+
export const session = sqliteTable("session", {
14+
id: text("id").primaryKey(),
15+
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
16+
token: text("token").notNull().unique(),
17+
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
18+
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
19+
ipAddress: text("ip_address"),
20+
userAgent: text("user_agent"),
21+
userId: text("user_id")
22+
.notNull()
23+
.references(() => user.id),
24+
});
25+
26+
export const account = sqliteTable("account", {
27+
id: text("id").primaryKey(),
28+
accountId: text("account_id").notNull(),
29+
providerId: text("provider_id").notNull(),
30+
userId: text("user_id")
31+
.notNull()
32+
.references(() => user.id),
33+
accessToken: text("access_token"),
34+
refreshToken: text("refresh_token"),
35+
idToken: text("id_token"),
36+
accessTokenExpiresAt: integer("access_token_expires_at", {
37+
mode: "timestamp",
38+
}),
39+
refreshTokenExpiresAt: integer("refresh_token_expires_at", {
40+
mode: "timestamp",
41+
}),
42+
scope: text("scope"),
43+
password: text("password"),
44+
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
45+
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
46+
});
47+
48+
export const verification = sqliteTable("verification", {
49+
id: text("id").primaryKey(),
50+
identifier: text("identifier").notNull(),
51+
value: text("value").notNull(),
52+
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
53+
createdAt: integer("created_at", { mode: "timestamp" }),
54+
updatedAt: integer("updated_at", { mode: "timestamp" }),
55+
});

apps/server/src/db/schema/todo.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
2+
3+
export const todo = sqliteTable("todo", {
4+
id: integer("id").primaryKey({ autoIncrement: true }),
5+
text: text("text").notNull(),
6+
completed: integer("completed", { mode: "boolean" }).default(false).notNull(),
7+
});

0 commit comments

Comments
 (0)