Full Stack TypeScript
Stacknaut is TypeScript end-to-end — frontend, backend, API, shared code, and database schemas. One language across the entire stack, with type safety from the database to the UI.
The Stack
| Layer | Technology | Role |
|---|---|---|
| Frontend | Vue 3 + Vite + Tailwind CSS | SPA with pre-rendering for SEO |
| Backend | Fastify + Node.js | Private API for the frontend |
| API | Fastify + Node.js | Public-facing API service |
| Database | PostgreSQL + Drizzle ORM | Type-safe queries and migrations |
| Shared | @myapp/shared | Types, schemas, and utilities |
| UI | shadcn-vue + Radix | Accessible, composable components |
| State | Pinia | Frontend state management |
| Routing | Vue Router | Client-side routing with pre-rendered pages |
Why These Choices
Vue 3 — Simpler mental model than React. The Composition API is powerful without the complexity of hooks rules. Templates are readable. The ecosystem (Pinia, Vue Router, VueUse) is cohesive and well-maintained.
Fastify — Faster than Express, with built-in TypeScript support, schema validation, and a plugin system that scales. The decorator pattern keeps things organized without heavy abstractions.
Drizzle ORM — SQL-like query builder that's fully type-safe. No magic, no runtime overhead, no query generation surprises. Schemas are plain TypeScript — your AI coding agent can read and extend them without special knowledge.
PostgreSQL — The most reliable relational database. Handles everything from a solo project to millions of rows. Runs as a Kamal accessory on the same server.
Project Structure
frontend/ — Vue 3 SPA
src/
views/ — Page components
components/ — Reusable UI components
stores/ — Pinia stores
lib/ — Utilities
router/ — Route definitions
backend/ — Fastify API (private, for frontend)
src/
controllers/ — Request handlers
routes/ — Route definitions
services/ — Business logic
plugins/ — Fastify plugins
lib/ — Utilities
api/ — Fastify API (public)
src/
controllers/
routes/
lib/
shared/ — Shared between all services
src/
schemas/ — Drizzle database schemas
index.ts — Exported types and utilities
Shared Code
The shared/ module (@myapp/shared) is the single source of truth for:
- Database schemas — Drizzle table definitions in
shared/src/schemas/ - Types — Shared TypeScript types used by frontend and backend
- Utilities — Functions used across services (e.g., credit calculations)
Import with the package alias:
import { users, magicLinks } from "@myapp/shared"
When you change a schema, all services that import from shared get type errors immediately — no drift between frontend expectations and backend reality.
Path Aliases
Both frontend and backend use @ as an alias for src/:
import { env } from "@/env"
import { LOGGER } from "@/plugins/logger"
No relative import chains (../../../lib/utils). Every import is clear about where it comes from.
Database Workflow
Schemas live in shared/src/schemas/. Edit the TypeScript schema, then generate and run migrations:
cd backend
pnpm run db:generate # Generate SQL migration from schema changes
pnpm run db:migrate # Apply pending migrations
pnpm run db:studio # Open Drizzle Studio (database GUI)
Migrations run automatically when the backend starts in production — no separate migration step during deployment.
What You Get
- TypeScript for all application code (frontend, backend, API, shared)
- Type-safe database queries with Drizzle ORM
- Shared types and schemas between frontend and backend
- Path aliases (
@/) in all services - Vue 3 Composition API with
<script setup> - Pinia stores for state management
- shadcn-vue components (accessible, customizable)
- Tailwind CSS with dark mode support
- Fastify with Zod request validation
- Auto-running migrations in production
- Pre-rendering for SEO pages
- PostHog analytics integration