docs: complete project research
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>
This commit is contained in:
243
.planning/research/STACK.md
Normal file
243
.planning/research/STACK.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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:**
|
||||
1. Accept upload (validate file type server-side, not just Content-Type header)
|
||||
2. Generate thumbnail with sharp (resize to max 1920px width, convert to WebP)
|
||||
3. Store original and thumbnail
|
||||
4. 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/stores` for 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:
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```dockerfile
|
||||
# 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 ci` for reproducible builds
|
||||
|
||||
## Search Implementation
|
||||
|
||||
Use **SQLite FTS5** (built-in full-text search):
|
||||
|
||||
```sql
|
||||
-- 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](https://github.com/sveltejs/svelte/releases) - Current stable version verified
|
||||
- [SvelteKit 2.50.1 Release](https://github.com/sveltejs/kit/releases) - Current stable version verified
|
||||
- [Hono 4.11.7 Release](https://github.com/honojs/hono/releases) - Current stable version verified
|
||||
- [Tailwind CSS v4.1 Documentation](https://tailwindcss.com/docs/installation) - Current version verified
|
||||
- [sharp Documentation](https://sharp.pixelplumbing.com/) - API and requirements verified
|
||||
- [better-sqlite3 GitHub](https://github.com/WiseLibs/better-sqlite3) - Performance claims verified
|
||||
- [SQLite FTS5 Documentation](https://www.sqlite.org/fts5.html) - Built-in full-text search
|
||||
- [Docker Node.js Best Practices](https://docs.docker.com/guides/nodejs/containerize/) - Official guide
|
||||
|
||||
### MEDIUM Confidence (Multiple Sources Agree)
|
||||
- [Drizzle vs Prisma Comparison](https://betterstack.com/community/guides/scaling-nodejs/drizzle-vs-prisma/) - Performance characteristics
|
||||
- [React vs Vue vs Svelte 2026](https://www.frontendtools.tech/blog/best-frontend-frameworks-2025-comparison) - Framework comparison benchmarks
|
||||
- [SQLite vs PostgreSQL Comparison](https://www.selecthub.com/relational-database-solutions/postgresql-vs-sqlite/) - Use case recommendations
|
||||
- [Node.js Backend Framework Comparison](https://betterstack.com/community/guides/scaling-nodejs/hono-vs-fastify/) - Express vs Fastify vs Hono
|
||||
- [SQLite Driver Benchmark](https://sqg.dev/blog/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*
|
||||
Reference in New Issue
Block a user