Docs

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

7a9a8821

© 2026 Stacknaut