Doenet is an educational technology platform — npm workspace monorepo (Node.js 24 required).
apps/api— Express REST API + Prisma + MySQLapps/app— React SPA (main client)apps/web— Astro static sitepackages/shared— types and utilities shared byapiandapp; must be built before either apppackages/e2e-tests— Cypress end-to-end testspackages/load-tests— Locust load tests
npm ci # install
npm run dev # shared watcher + api (3000) + app (8000) + web (4321)
npm run build # all workspaces
npm run format && npm run lint # always run before finishing work
npm run db:setup # migrate + seed (dev only — first-time or after schema changes)
npm test --workspace @doenet-tools/api # Vitest unit tests (append filename for single file)
npm run test:all --workspace @doenet-tools/app # Cypress component tests, headless
npm run test:all --workspace @doenet-tools/e2e-tests # Cypress e2e tests, headless (needs dev servers)Always run Prettier and ESLint on changed files. Tests must pass before committing.
Browser → Vite (8000) → /api/* proxy → Express (3000) → Prisma → MySQL
In production, app is built as static files served by Express.
apps/api/— seeapps/api/AGENTS.mdfor route, error, and UUID conventionsapps/app/— seeapps/app/AGENTS.mdfor routing and mutation patternsapps/web/— seeapps/web/AGENTS.mdfor Astro stack and blog frontmatter schemapackages/shared/— seepackages/shared/AGENTS.mdfor when to add shared types/utilities
Development uses a fork workflow. Push branches to origin (your fork), then open a PR targeting upstream/main. Merged PRs deploy to production after human sign-off.
Database and API changes must follow the expand-migrate-contract pattern: each merged PR must be safe to deploy on its own, so add new columns/endpoints before removing old ones across separate PRs.
- TypeScript strict mode is enforced across all workspaces
apps/app/src/types.tsandapps/api/src/types.tsare intentionally identical — update both; platform-specific differences go intypes_module_specific.ts