Skip to content

Commit b21a7a2

Browse files
committed
Revise README.md for clarity and structure: Updated tech stack details, improved setup instructions, and enhanced type safety explanations. Added architecture diagrams and refined common commands for better user guidance.
1 parent 77ee163 commit b21a7a2

File tree

1 file changed

+140
-66
lines changed

1 file changed

+140
-66
lines changed

README.md

Lines changed: 140 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,197 @@
11
# SojuStack
22

3-
My constantly updated opinionated stack for constructing new Fullstack(Web/Mobile/Backend) applications.
3+
Opinionated, production-minded full-stack starter with strong TypeScript ergonomics and end-to-end type safety.
44

5-
## Getting Started
5+
## Tech Stack
66

7-
I've provided both automated and manual setup options to fit your workflow.
7+
### Monorepo and Tooling
88

9-
### 🚀 Quick Start (Automated)
9+
- `pnpm` workspaces
10+
- `turbo` task orchestration
11+
- `TypeScript` everywhere
12+
- `oxlint` + `oxfmt`
13+
- `lefthook` (pre-push checks)
1014

11-
Only use quick-start for trying the stack out initially. Don't use this during actual development.
15+
### Web (`apps/web`)
1216

13-
```sh
14-
$ pnpm dev:setup
17+
- `TanStack Start` + `TanStack Router`
18+
- `TanStack Query`
19+
- `openapi-fetch` generated client types
20+
- `Tailwind CSS` + `shadcn` + `@base-ui/react`
21+
- `better-auth` client integration
22+
23+
### API (`apps/api`)
24+
25+
- `NestJS` (v11)
26+
- `Drizzle ORM` + `drizzle-kit`
27+
- `PostgreSQL`
28+
- `Valkey`/Redis-compatible caching via `Keyv`
29+
- `Better Auth`
30+
- OpenAPI generation (`@nestjs/swagger` + `openapi-typescript`)
31+
32+
### Local Infra (`docker-compose.yaml`)
33+
34+
- `Postgres`
35+
- `Valkey`
36+
- `Mailpit`
37+
- `RustFS` (S3-compatible object storage)
38+
39+
## Architecture
40+
41+
```mermaid
42+
flowchart LR
43+
U[User] --> W[Web App<br/>TanStack Start]
44+
W -->|HTTP + Cookies| A[API<br/>NestJS]
45+
A --> P[(PostgreSQL)]
46+
A --> V[(Valkey)]
47+
A --> S[(RustFS / S3-compatible)]
48+
A --> M[Mailpit]
1549
```
1650

17-
### 🛠️ Manual Setup (Recommended)
51+
## Type-Safe Flow
1852

19-
For more control over the setup process, run the following in the order listed at the root of the project.
53+
```mermaid
54+
sequenceDiagram
55+
participant FE as Web
56+
participant API as NestJS API
57+
participant OAS as OpenAPI Types
2058
21-
```sh
22-
# 1. Create environment files
23-
$ cp apps/api/.env.example apps/api/.env
24-
$ cp apps/web/.env.example apps/web/.env
59+
FE->>API: Request typed by openapi-fetch client
60+
API-->>FE: Response typed by generated schema
61+
API->>OAS: Generate/update openapi.d.ts
62+
OAS-->>FE: Compile-time contract updates
63+
```
2564

26-
# 2. Install dependencies
27-
$ pnpm install
65+
## Getting Started
2866

29-
# 3. Start Docker services
30-
$ docker-compose up -d
67+
### Quick Trial (Automated)
3168

32-
# 4. Setup database
33-
$ pnpm -C ./apps/api db:push
69+
Use this only for first-time trial runs:
3470

35-
# 5. Start development servers
36-
$ pnpm dev
71+
```sh
72+
pnpm dev:setup
3773
```
3874

39-
#### Default Service URLs
75+
### Manual Setup (Recommended)
76+
77+
```sh
78+
# 1) Create env files
79+
cp apps/api/.env.example apps/api/.env
80+
cp apps/web/.env.example apps/web/.env
81+
82+
# 2) Install dependencies
83+
pnpm install
84+
85+
# 3) Start local infrastructure
86+
docker-compose up -d
87+
88+
# 4) Apply DB schema
89+
pnpm -C apps/api db:push
4090

91+
# 5) Start apps
92+
pnpm dev
4193
```
42-
web: http://localhost:3000
43-
api: http://localhost:8080
44-
mailpit: http://localhost:8025
94+
95+
## Default Local Endpoints
96+
97+
```txt
98+
web: http://localhost:3000
99+
api: http://localhost:8080
100+
mailpit: http://localhost:8025
45101
postgres: postgres://postgres:postgres@localhost:5432/postgres
46-
redis: redis://localhost:6379
47-
minio: http://localhost:9000
102+
valkey: redis://localhost:6379
103+
rustfs: http://localhost:9000
48104
```
49105

50-
## E2E Typesafety
106+
## Common Commands (Root)
51107

52-
When developing applications with a smaller team(or solo) that tends to pivot, E2E typesafety is pretty imparative to develop with confidence. This stack acheives E2E type-safety through a few different means,
108+
```sh
109+
pnpm dev # run web + api dev tasks via turbo
110+
pnpm build # build all packages
111+
pnpm lint # oxlint type-aware run
112+
pnpm lint:fix # apply lint fixes
113+
pnpm typecheck # turbo typecheck
114+
pnpm format # oxfmt
115+
pnpm api:openapi:generate
116+
```
53117

54-
1. A monorepo where types can be shared.
55-
2. OpenAPI(Swagger) docs actively generated.
56-
3. OpenAPI-Fetch client on the frontends.
118+
## End-to-End Type Safety
57119

58-
**There is no need to regnerate types in the SojuStack, this is done automatically.** The only necessity is to ensure DTO's have their correct OpenAPI annotations.
120+
SojuStack keeps API and frontend contracts aligned by default:
59121

60-
This is achieved by a non-blocking helper function that listens and regenerates the `openapi.d.ts` file upon any changes - instantly reflecting on all frontend clients. Go ahead, change an endpoint or DTO in NestJS and view the errors in the frontend clients.
122+
- Shared monorepo context
123+
- OpenAPI docs and generated type definitions
124+
- Typed API usage through `openapi-fetch`
61125

62-
## Whats Inside and Why?
126+
In practice: update a DTO/route in API and frontend compile errors surface immediately where contracts changed.
63127

64-
Hopefully this helps understand my logic and reasoning behind my choices in technology.
128+
## Why This Stack
65129

66130
### Axioms
67131

68132
1. Does it solve the problem?
69-
2. Is it tested, stable, well documented, and battle-proven?
70-
3. No lock-in or ability to migrate away from the choice at anytime.
71-
4. Will it scale with my needs?
72-
5. Is the DX well thought out?
133+
2. Is it battle-tested and documented?
134+
3. Can I migrate away without pain?
135+
4. Does it scale with me?
136+
5. Is the developer experience actually good?
137+
6. Does it work well with AI(yeah, we're here so might as well)
73138

74139
### Backend
75140

76-
#### NestJS (Framework)
141+
#### NestJS
77142

78-
NestJS is the first to check all of the boxes above. It's on its v11 release, its proven itself for over a decade, it's incredibly easy to scale with many options available, and its documentation is fantastic.
143+
It checks all the boxes for me: mature, structured, testable, and easy to grow.
144+
People call it verbose; I call it explicit. I prefer that when business logic gets real.
79145

80-
It gets a bad rap in the node world for being verbose or from those who hate "OOP" in a "functional language". I for one embrace OOP in JS as it naturally documents your applications domain and business logic, keeps your application easily testable, and idk...its the way I mentally model things already so it translates well in my case. Also, have you ever written a monad?
146+
#### PostgreSQL
81147

82-
Testability is also a massive incentive for building in NestJS. I'm not the largest fan of TDD or unit testing, but in some cases it sure does come in handy. And when you finally have the needs to write tests, you'll be thankful for an environment that makes it painless.
148+
I'm a relational DB person. Postgres has the right defaults and enough headroom that I rarely regret choosing it.
83149

84-
#### Postgres (Database)
150+
#### Drizzle
85151

86-
I'm a relational database type of guy. NoSQL databases have their role and place in the world but by large and whole you can't go wrong with Postgres. The only other real option is MySQL, which has some benefits, but in my experience I always end up wanting to reach for a feature or plugin it lacks.
152+
This is the SQL-first ORM experience I actually enjoy.
153+
Minimal magic, typed queries, reviewable migrations, and no giant abstraction tax.
87154

88-
#### Drizzle (ORM)
155+
#### Valkey (Redis-compatible cache)
89156

90-
Drizzle is early, sure - but its also very, very, very simple. Its a query builder with migrations. I've had more confidence using Drizzle than the industry standard TypeORM by a long shot.
91-
If you know SQL, you know Drizzle and I like it for a few reasons,
157+
Simple, fast, and drop-in for Redis workflows.
158+
With `Keyv` in the API layer, swapping providers is mostly config-level work.
92159

93-
1. Its basic and simple; there is no magic. You write SQL like typescript and it generates pure SQL migrations to review. Infinite room to grow and no regrets to be had(ALWAYS REVIEW YOUR SQL MIGRATIONS MANUALLY, THIS IS TRUE FOR ANYTHING).
94-
2. It offers a type-safe, SQL-first approach that strikes an ideal balance between raw SQL flexibility and modern TypeScript ergonomics.
95-
3. It doesn't need an ecosystem like traditional ORMS. Its built to write sql and typesafe sql you shall write.
160+
#### Better Auth
96161

97-
#### Redis (Cache)
162+
"Don't roll your own crypto" is still the rule.
163+
Better Auth gives enough abstraction to move fast without boxing me in.
98164

99-
Substitute this for ValKey if you wish. The cache manager in NestJS uses KeyV as an interface so the only migration needed is to change the URL.
165+
#### RustFS (S3-compatible object storage)
100166

101-
#### Better-Auth (Auth)
167+
Local-first, self-hostable, and S3-compatible.
168+
If I move to managed object storage later, it's mostly endpoint/credential changes.
102169

103-
I'm a fan of "roll your own auth, not your own encryption". Its really not that hard to setup, but after evaluating Better-Auth I feel comfortable enough to include it as my auth solution. It does what I need it to do, provides an adequate level of abstraction, then gets out of the way. I've tested implementing better-auth then migrating away to a "self-rolled" auth and its **incredidbly** simple which gives me more confidence to build with it.
170+
### Frontend
104171

105-
#### MinIO (Storage)
172+
#### TanStack Start + Router
106173

107-
Minio is just self-hosted open source storage. Its fully S3 compatible so if you ever wanna switch over, its just a url change. The reason im a fan of MinIO is it keeps me out of the AWS console, lets me emulate my entire stack locally, and I can throw it on any VPS right next to my other services. Throw a CDN infront of it and it's quicker than S3.
174+
Fantastic routing model and good long-term ergonomics.
175+
File-based routes + typed APIs make iteration fast and refactors safer.
108176

109-
### Frontend
177+
#### TanStack Query
178+
179+
The default for server state in React apps for a reason.
180+
181+
#### shadcn + Base UI + Tailwind
182+
183+
Composable primitives, easy customization, and no hard lock-in to a black-box UI kit.
110184

111-
#### Tanstack Start (Framework)
185+
### Monorepo + Tooling
112186

113-
I was hesitant to build ontop of this as its pre v1.0 release, but after seeing how far its come in such a short amount of time, the "breaking changes" guide being so incredibly well updated and documented, and its DX - its too good to let go.
187+
#### pnpm + Turbo
114188

115-
This is the ONLY pre-release tech i'm willing to bet on for production at this time. I left for Svelte after seeing the direction of React and the horrific war-crimes Vercel/NextJS committed. TanStack Start brought me back to React.
189+
Fast workspace installs, clean task orchestration, and predictable monorepo workflows.
116190

117-
#### Tanstack Query (Data Fetching)
191+
#### Oxlint + Oxfmt
118192

119-
IDK what to say on this one, its obvious. If you remember what life was like before Tanstack Query having to fetch data with redux/thunks - you'd be a donator to the project like I am.
193+
Very fast lint/format feedback loops with type-aware checks where it matters.
120194

121-
#### ShadCN (UI)
195+
#### Lefthook
122196

123-
I have no preference in UI components. Use what you like and there's not really a wrong answer - this is purely what you enjoy and don't let anyone tell you differently. I use ShadCN for the same reasons i mentioned about every other tech above. It does what I need, gets out of my way when I want it to, and I can easily migrate away from it without fear of lock-in.
197+
Simple guardrails before push (`lint` + `typecheck`) so broken code is harder to ship.

0 commit comments

Comments
 (0)