Files: - STACK.md - SvelteKit + SQLite + TypeScript stack recommendation - FEATURES.md - Feature landscape with MVP definition - ARCHITECTURE.md - Modular monolith architecture with repository pattern - PITFALLS.md - Critical pitfalls and prevention strategies - SUMMARY.md - Executive synthesis with roadmap implications Key findings: - Stack: SvelteKit 2.50.x + Svelte 5.49.x with SQLite and better-sqlite3 for single-user simplicity - Architecture: Modular monolith with content-addressable image storage, FTS5 for search - Critical pitfall: Store images on filesystem (not DB) from Phase 1 to avoid painful migration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
11 KiB
Stack Research
Domain: Personal Task/Notes Web App (self-hosted, containerized, single-user) Researched: 2026-01-29 Confidence: HIGH
Recommended Stack
Core Technologies
| Technology | Version | Purpose | Why Recommended |
|---|---|---|---|
| SvelteKit | 2.50.x | Full-stack framework | Best DX for solo/small projects. Svelte 5's runes provide explicit reactivity. No virtual DOM = smaller bundles and faster performance. Built-in routing, SSR, and API routes. Perfect for self-hosted single-user apps. |
| Svelte | 5.49.x | UI framework | Compiles to vanilla JS with minimal runtime. 30% faster load times vs React/Vue per 2026 benchmarks. Smaller bundle = faster on any device. |
| SQLite | 3.x | Database | Zero-config, serverless, single-file database. Ideal for single-user apps. FTS5 provides full-text search built-in. No separate database service to manage in Docker. |
| better-sqlite3 | 12.x | SQLite driver | 20-39% faster than alternatives. Synchronous API is simpler for single-user apps. Native bindings outperform WASM alternatives. |
| TypeScript | 5.x | Language | Type safety catches bugs early. First-class Svelte/SvelteKit support. Industry standard for modern web development. |
Supporting Libraries
| Library | Version | Purpose | When to Use |
|---|---|---|---|
| sharp | 0.34.x | Image processing | Always - for thumbnail generation, image optimization. 4-5x faster than ImageMagick. Handles JPEG, PNG, WebP, AVIF. |
| Drizzle ORM | 0.45.x (stable) | Database ORM | Recommended for type-safe SQL. Code-first schema in TypeScript. Lightweight (~7.4kb), no runtime overhead. Excellent SQLite support. |
| Tailwind CSS | 4.1.x | Styling | Always - utility-first CSS with zero runtime overhead. Automatic purging keeps CSS tiny. Great for rapid prototyping. |
| Zod | 3.x | Validation | Form validation, API input validation. Integrates with SvelteKit's form actions. TypeScript-first schema validation. |
| nanoid | 5.x | ID generation | URL-safe unique IDs for tasks/notes. Smaller and faster than UUID. |
Development Tools
| Tool | Purpose | Notes |
|---|---|---|
| Vite | Build tool | Built into SvelteKit. Fast HMR, optimized builds. |
| ESLint + Prettier | Code quality | Use eslint-plugin-svelte for Svelte-specific rules. |
| Vitest | Testing | Vite-native testing. Fast, compatible with SvelteKit. |
| Docker | Containerization | Multi-stage builds for minimal production images. Use node:22-alpine base. |
Infrastructure (Self-Hosted)
| Technology | Version | Purpose | Why Recommended |
|---|---|---|---|
| Node.js | 22 LTS | Runtime | Current LTS. Required for better-sqlite3 native bindings. |
| Docker | Latest | Container | Multi-stage builds reduce image size 10x. Alpine base keeps footprint small (~70MB). |
| Caddy | 2.x | Reverse proxy (optional) | Simple config, automatic HTTPS. Built-in basic auth if needed. |
Installation
# Create SvelteKit project
npx sv create taskplaner
cd taskplaner
# Core dependencies
npm install better-sqlite3 drizzle-orm sharp nanoid zod
# Dev dependencies
npm install -D drizzle-kit @types/better-sqlite3 tailwindcss @tailwindcss/vite
File Storage Strategy
For image attachments, use local filesystem storage:
/data/
database.sqlite # SQLite database
attachments/ # Image files
{year}/{month}/ # Organized by date
{nanoid}.webp # Optimized images
Why filesystem over object storage:
- Single-user app doesn't need S3 complexity
- Easy to backup (just copy the /data folder)
- No external dependencies
- Docker volume mount makes it simple
Image processing pipeline:
- Accept upload (validate file type server-side, not just Content-Type header)
- Generate thumbnail with sharp (resize to max 1920px width, convert to WebP)
- Store original and thumbnail
- Save path reference in SQLite
Alternatives Considered
| Recommended | Alternative | When to Use Alternative |
|---|---|---|
| SvelteKit | Next.js | If you need React ecosystem libraries or team already knows React. |
| SvelteKit | Hono + separate frontend | If building API-first architecture for mobile apps later. |
| SQLite | PostgreSQL | If you expect multiple concurrent users or need advanced features like JSONB operators. |
| better-sqlite3 | Drizzle with LibSQL | If you want Turso cloud sync in the future. |
| Drizzle ORM | Prisma | If you prefer schema-first approach and don't mind slower cold starts. |
| Tailwind CSS | CSS Modules | If you dislike utility classes and prefer scoped traditional CSS. |
| sharp | Thumbor | If you need a dedicated image service with caching CDN. Overkill for single-user. |
What NOT to Use
| Avoid | Why | Use Instead |
|---|---|---|
| Express.js | Outdated, slow, development stalled. No need for separate backend with SvelteKit. | SvelteKit API routes (or Hono if you must separate) |
| Prisma | Heavier runtime, slower cold starts, schema-first adds build step. | Drizzle ORM - lighter, faster, code-first |
| MongoDB | Overkill for structured task/note data. No ACID by default. Adds container complexity. | SQLite - simpler, faster for this use case |
| Redis | Unnecessary for single-user app. SQLite is fast enough for session/cache. | SQLite in-memory or just application state |
| CSS-in-JS (styled-components, Emotion) | Runtime overhead, larger bundles, complexity. | Tailwind CSS - zero runtime, tiny output |
| TypeORM | Buggy, inconsistent TypeScript support, heavy. | Drizzle ORM |
| Sequelize | Old patterns, poor TypeScript support. | Drizzle ORM |
| sql.js | 50-60% slower than native SQLite. Only use if browser-side SQLite needed. | better-sqlite3 for Node.js server |
| node-sqlite3 | Callback-based API, slower than better-sqlite3. | better-sqlite3 |
| Full auth systems (Keycloak, Authentik) | Massive overkill for single-user. Adds container complexity. | Simple session with hashed password in SQLite, or Caddy basic auth |
Stack Patterns by Variant
If you want maximum simplicity:
- Skip Drizzle, use better-sqlite3 directly with raw SQL
- Use SvelteKit's built-in
$app/storesfor state instead of external state management - Single Dockerfile with embedded SQLite
If you might add mobile app later:
- Consider separating API with Hono
- Add JWT-based auth from the start
- Design REST API endpoints alongside SvelteKit routes
If you want offline-first/PWA:
- Add service worker (SvelteKit supports this)
- Consider IndexedDB for client-side cache
- Implement sync strategy for when online
Authentication Recommendation
For single-user self-hosted, keep it simple:
// Simple password hash stored in SQLite or environment variable
// Use Argon2 or bcrypt for password hashing
import { hash, verify } from '@node-rs/argon2';
// Session stored as httpOnly cookie
// SvelteKit hooks handle session validation
Do NOT:
- Use full OAuth providers (Google, GitHub) - unnecessary for single-user
- Implement JWT tokens - session cookies are simpler and more secure for web-only
- Use external auth services - adds complexity and external dependencies
Consider:
- Caddy's built-in basic auth as the simplest option (one line of config)
- Environment variable for password hash (no database needed for auth)
Version Compatibility
| Package | Compatible With | Notes |
|---|---|---|
| better-sqlite3@12.x | Node.js 18.17+ or 20.3+ | Requires native compilation or prebuilt binaries |
| sharp@0.34.x | Node.js 18.17+ or 20.3+ | Uses libvips, prebuilts available for common platforms |
| Drizzle@0.45.x | better-sqlite3@11+ | Use drizzle-orm/better-sqlite3 adapter |
| SvelteKit@2.50.x | Svelte 5.x | Svelte 5 is peer dependency |
| Tailwind@4.1.x | Vite (built into SvelteKit) | Use @tailwindcss/vite plugin |
Docker Configuration
# Multi-stage build for minimal production image
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine AS production
WORKDIR /app
# Install only production dependencies
COPY package*.json ./
RUN npm ci --omit=dev
# Copy built application
COPY --from=builder /app/build ./build
# Create non-root user
RUN adduser -D appuser && chown -R appuser /app
USER appuser
# Data volume for SQLite and attachments
VOLUME /app/data
EXPOSE 3000
CMD ["node", "build"]
Key practices:
- Multi-stage build reduces image from ~500MB to ~70MB
- Non-root user for security
- Volume mount for persistent data
- Alpine base for minimal attack surface
- Use
npm cifor reproducible builds
Search Implementation
Use SQLite FTS5 (built-in full-text search):
-- Create FTS5 virtual table
CREATE VIRTUAL TABLE notes_fts USING fts5(
title,
content,
content='notes',
content_rowid='id'
);
-- Search with ranking
SELECT * FROM notes_fts WHERE notes_fts MATCH 'search query' ORDER BY rank;
Why FTS5 over Meilisearch/Typesense:
- Zero dependencies - built into SQLite
- 6-8x smaller index size than Meilisearch
- Much lower memory usage (~75MB vs 800MB)
- Simpler architecture - just SQL queries
- Sufficient for personal notes (thousands, not millions of documents)
Sources
HIGH Confidence (Official Documentation)
- Svelte 5.49.0 Release - Current stable version verified
- SvelteKit 2.50.1 Release - Current stable version verified
- Hono 4.11.7 Release - Current stable version verified
- Tailwind CSS v4.1 Documentation - Current version verified
- sharp Documentation - API and requirements verified
- better-sqlite3 GitHub - Performance claims verified
- SQLite FTS5 Documentation - Built-in full-text search
- Docker Node.js Best Practices - Official guide
MEDIUM Confidence (Multiple Sources Agree)
- Drizzle vs Prisma Comparison - Performance characteristics
- React vs Vue vs Svelte 2026 - Framework comparison benchmarks
- SQLite vs PostgreSQL Comparison - Use case recommendations
- Node.js Backend Framework Comparison - Express vs Fastify vs Hono
- SQLite Driver Benchmark - better-sqlite3 performance verified
LOW Confidence (Single Source, Verify Before Using)
- Drizzle ORM 1.0 release timing - still in beta, stable is 0.45.x
- Sharp 0.35.0 - currently RC, not stable
Stack research for: Personal Task/Notes Web App Researched: 2026-01-29