Documentation

Run Speed Doctor yourself

Speed Doctor is a pnpm monorepo with a Next.js front-end, a NestJS API, and a background worker that drives Playwright, Lighthouse and AI. This guide takes you from clone to a working local install, and on to deployment.

What's inside

The project is split into three runnable apps and a set of focused packages:

  • apps/web — Next.js 15 front-end (this site).
  • apps/api — NestJS + Fastify REST API that accepts audit requests and streams progress.
  • apps/worker — NestJS worker that runs the scan pipeline off a BullMQ queue.
  • packages/* — scanner (Playwright), lighthouse-engine, dom-analyzer, root-cause-engine, priority-engine, ai-engine, db (Drizzle), queue (BullMQ) and shared-types.

Prerequisites

  • Node.js 20+ and pnpm 11+ (corepack enable will provision pnpm).
  • A PostgreSQL database — a free Neon project works out of the box.
  • A Redis instanceUpstash (cloud) or the bundled Docker Compose service for local dev.
  • Chrome/Chromium — installed automatically by Playwright; needed by the worker.
  • Optional: an OpenRouter API key for AI explanations (without it, built-in templates are used).

Quick start

  1. 1

    Clone and install.

    terminal
    git clone https://github.com/dev-tanvu/Speed-Doctor.git
    cd Speed-Doctor
    pnpm install
  2. 2

    Create your environment file from the template and fill in the values.

    terminal
    cp .env.example .env   # Windows: copy .env.example .env

    See Environment variables below for what each one means.

  3. 3

    Start Redis (skip if you use Upstash and set REDIS_URL accordingly).

    terminal
    docker compose up -d redis
  4. 4

    Create the database schema against your Postgres database.

    terminal
    pnpm --filter @speed-doctor/db exec drizzle-kit push
  5. 5

    Run everything (web, API and worker together).

    terminal
    pnpm dev

    The app is now at http://localhost:3000, the API at http://localhost:3001.

Environment variables

All variables live in a single root .env. The web app only needs NEXT_PUBLIC_API_URL; the rest are read by the API and worker.

VariableRequiredPurpose
DATABASE_URLYesPostgres connection string (append ?sslmode=require for Neon).
REDIS_URLYesRedis connection (use rediss:// for Upstash TLS).
OPENROUTER_API_KEYNoEnables AI explanations; falls back to templates if unset.
OPENROUTER_MODELNoOverride the model (default openai/gpt-4o-mini).
WORKER_CONCURRENCYNoHow many audits the worker runs at once (default 1).
ALLOWED_ORIGINSNoComma-separated CORS allowlist (default http://localhost:3000).
RATE_LIMIT_MAXNoMax audit requests per IP per minute (default 10).
NEXT_PUBLIC_API_URLYesPublic URL of the API the browser calls.
Never commit .env
Your real credentials belong only in .env, which is git-ignored. Commit changes to .env.example (placeholders) instead. See the security policy.

Using the app

  1. 1

    Open http://localhost:3000 and enter a website URL.

  2. 2

    Pick a test environment: Both (mobile + desktop), Mobile, or Desktop.

  3. 3

    Watch the live progress as the page is scanned, measured, analysed and diagnosed.

  4. 4

    Read the report: category scores, Core Web Vitals, the heaviest assets, and each issue with a plain-English and a developer view. In “Both” mode, switch devices with the tabs at the top.

How an audit works

Submitting a URL creates an audit run and enqueues a job. The worker then:

  • Scans the page with Playwright (real Chromium), capturing HTML, assets and timings.
  • Measures Core Web Vitals with Lighthouse, in an isolated child process.
  • Analyses the DOM with six detectors (images, fonts, JS, video, third-party, DOM size).
  • Correlates findings to the metrics they hurt and ranks root causes.
  • Explains each issue via AI (or templates) and saves the report.

For Both mode the front-end fires two runs (one per device) and shows a combined progress view, then device tabs.

Deployment

Front-end → Vercel. Import the repo, set the project root to apps/web, add NEXT_PUBLIC_API_URL pointing at your deployed API, and Vercel builds and hosts it automatically on every push.

The API and worker can't run on Vercel
The worker is a long-running process that launches real Chrome (Playwright + Lighthouse), so it needs a normal Node host — Railway, Render, Fly.io or a VPS — plus managed Redis (Upstash) and Postgres (Neon). Deploy apps/api and apps/worker there, then point the Vercel front-end at the API URL via ALLOWED_ORIGINS and NEXT_PUBLIC_API_URL.
  • apps/apipnpm --filter @speed-doctor/api build then pnpm --filter @speed-doctor/api start.
  • apps/workerpnpm --filter @speed-doctor/worker build then pnpm --filter @speed-doctor/worker start.

Troubleshooting

  • “performance mark has not been set” — Lighthouse runs in a child process to prevent this; make sure the worker was restarted after pulling changes.
  • Next dev shows missing-chunk / ENOENT errors — only run one next dev at a time; the dev script uses Turbopack to avoid cache corruption.
  • CORS errors in the browser — set ALLOWED_ORIGINS on the API to include your web origin.
  • Scores look different from PageSpeed Insights — that's expected; see Why the gap?

Next steps

Want to help improve Speed Doctor? Read the contribution guide or open an issue on GitHub.