Boring Docs

Project Structure — Where Everything Lives

Providers, libs, models, thin routes, pages, components, config, data, scripts.

In this guide

Project Structure

Convention over configuration. See CONVENTIONS.md — this doc mirrors it.

Top level

  • providers/db/<engine>/ — one folder per database (sqlite, mongodb, supabase). Same exports everywhere. Plugged in via providers/db/index.js.
  • providers/billing/<rail>.js — one file per rail (stripe, lemonsqueezy, dodo). Unified surface: createCheckoutSession, createPortal, verifyAndParse. Registry: providers/billing/index.js.
  • providers/billing/access.js — shared grantAccess / revokeAccess. Only place money touches users/orders.
  • providers/analytics/track() write path + stats.js read path on separate analytics.sqlite.
  • libs/ — runtime helpers: config.js (toggles/limits), paywall.js (requirePaidUser), security.js (safeUrl, assertActorIntegrity, rateLimit, timeAgo), telemetry.js (scream alerts), admin.js, next-auth.js, stripe.js, resend.js, blog.js, seo.js, voter.js, mongo.js. libs/mongoose.js is a frozen re-export shim.
  • models/*.js — frozen one-line re-exports over providers/db/index.js. Never add logic here.
  • app/api/<thing>/route.js — thin handlers only: validate → auth → one provider call → JSON. ≤ 40 lines per handler.
  • app/(pages)/<route>/page.js — public pages; private ones under app/(pages)/dashboard/. Server components, data at top, props down.
  • components/<Name>.js — presentational only, Tailwind, props in. No DB/driver imports in client components.
  • config/ — domain configs (app, billing, auth, email, theme, services, paths, paywall, navigation) re-exported by config/index.js and root config.js.
  • data/blog/<slug>.md — markdown posts with frontmatter. data/docs/*.md — these guides.
  • scripts/<verb>-<noun>.sh — wired as npm run <verb>:<noun>, sh-compatible, set -eu.
  • nginx/<name>.conf — deploy targets with __PLACEHOLDERS__.
  • .agents/ + AGENTS.md — agent operating manual and skills.

Adding anything

Creating… Put it in Shape
New database providers/db/<name>/ + 1 line in registry connect, User, Board, Lead, Post, Vote, Comment, Product, Order
New entity Same exports in every engine + delegate + models/<E>.js shim { create, findById/getById, list*, set*, delete* } → plain { _id, id, … }
New rail providers/billing/<name>.js + 1 registry line createCheckoutSession, createPortal, verifyAndParse
New endpoint app/api/<thing>/route.js validate → auth → provider → JSON
New page app/(pages)/<route>/page.js server component
New UI piece components/<Name>.js props in, JSX out

No new top-level folders without updating CONVENTIONS.md.