Linkfolio is a Nuxt app for building collections of links and sharing them via a public slug — optionally password-protected — with automatic link-preview scraping.
- Nuxt 4 / Vue 3, TypeScript
- Vuetify (via
vuetify-nuxt-module) — UI - Neon Serverless Postgres + Drizzle ORM — database
- Better Auth (
better-auth/vue), proxied to Neon Auth — authentication - Vercel Blob — image upload storage; Cloudinary via
@nuxt/image— image delivery/transforms @nuxtjs/i18n— English/Russian@sentry/nuxt— error monitoring- Vitest +
@nuxt/test-utils— unit testing - Playwright — end-to-end testing
- Storybook + Chromatic — component development, visual testing
- ESLint + Prettier + Husky/lint-staged — linting, formatting, pre-commit hooks
- dependency-cruiser — circular-import and app/server boundary checks, dependency graph visualization
- Node version pinned in
.nvmrc— runnvm use - pnpm (see
pnpm-lock.yaml) - A Neon account/project (for Postgres + Neon Auth)
-
Install dependencies:
pnpm install
-
Set up environment variables. Either copy
.env.exampleto.envand fill in the values from the Neon console, or, if the project is already linked (see.neon), pull them automatically:npx neon env pull
Variable Purpose DATABASE_URLPooled Postgres connection, used by the app at runtime DATABASE_URL_UNPOOLEDDirect Postgres connection, used for Drizzle migrations NEON_BRANCHLinked Neon branch name NEON_AUTH_BASE_URLNeon Auth backend URL, proxied by server/api/auth/[...all].tsNEON_AUTH_JWKS_URLNeon Auth JWKS endpoint BETTER_AUTH_API_KEYNeon Auth API key BLOB_READ_WRITE_TOKENVercel Blob token for image uploads (Vercel dashboard → Storage → Blob store, or vercel env pull)These values are project secrets — never expose them to client-side code.
Running the Playwright e2e suite needs extra env in
.env.e2e.local(gitignored, layered on top of.env):Variable Purpose NEON_API_KEYNeon API key, used to call the branch-restore API NEON_TEST_BRANCH_IDPersistent e2e-testNeon branch the tests run againstNEON_RESET_SOURCE_BRANCH_IDSchema-only e2e-basebranche2e-testis restored from before/after each runTEST_DATABASE_URLConnection string for e2e-test, used as the app'sDATABASE_URLunder PlaywrightRegular
.env(DATABASE_URL,NEON_AUTH_*,BETTER_AUTH_API_KEY) is still needed too —password-reset.spec.tsreads the reset token straight from prod'sneon_auth.verification. Neon Auth is project-scoped, not per-branch, so e2e signups also land real rows in prod'sneon_auth.user; runpnpm cleanup:e2e-usersperiodically to clean these up (dry-run by default, append-- --yesto actually delete). -
Run database migrations:
pnpm db:migrate
(
pnpm db:generateregenerates migration files after changingserver/db/schema.ts.) -
(Optional) Sentry source map uploads.
pnpm builduploads source maps via@sentry/nuxt's build plugin, which needs an auth token separate from.env. Copy.env.sentry-build-plugin.exampleto.env.sentry-build-pluginand setSENTRY_AUTH_TOKEN(from Sentry → org "home-nbh" → project "linkfolio"). Not required forpnpm dev.
Start the development server on http://localhost:3000:
pnpm devpnpm test # or test:coverage — CI's gate, enforces coverage floor
pnpm spec-ratchet # every model/server-utils/api file has a colocated testpnpm exec playwright install # first time only, installs browsers
pnpm test:e2e # runs e2e/*.spec.ts against local dev serverSingle worker, not parallel — spec files share one dev server and one Neon Auth backend/test branch. Runs in CI on every PR and push to main (.github/workflows/e2e.yml), uploading a report artifact on failure.
pnpm lint # or lint:fix
pnpm format # or format:fix
pnpm type-checkpnpm dep-check # CI gate: no circular imports, no app/**<->server/** imports
pnpm dep-graph # prints a Mermaid diagram to stdout — paste into a PR/Markdown
pnpm dep-graph:archi # renders dependency-graph.svg (needs Graphviz's `dot` locally)Requires Node ^22||^24||>=26 (dependency-cruiser's own supported range) — the project's pinned version (.nvmrc, v26.7.0) already satisfies this, so nvm use (see Prerequisites) is enough if your default Node is outside that range. See .dependency-cruiser.cjs and CLAUDE.md for what these rules cover and why.
pnpm storybook # dev server at http://localhost:6006
pnpm build-storybookVisual tests run via Chromatic on every push/PR (see .github/workflows/chromatic.yml). Published Storybook: https://main--6a6f82979edc629ca6aeb6b4.chromatic.com/
Build the application for production:
pnpm buildLocally preview the production build:
pnpm previewOr generate a static build:
pnpm generateCheck out the deployment documentation for more information.
The app follows Feature-Sliced Design under app/, with a full CRUD API for collections, links, and sharing under server/api/. See CLAUDE.md for detailed architecture and conventions.