This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Yarn 4 (Berry) workspaces monorepo with three apps:
apps/postgres— Docker Compose wrapper for the Postgres 16 database the API depends on. DBtest, usercafe, passwordcafe, port5432.apps/nestjs-example— NestJS 11 REST API (port3001) with TypeORM, Postgres, JWT/Passport auth, Swagger (/api), Terminus health (/health), and an Apollo GraphQL endpoint (auto-generatesapps/nestjs-example/schema.gql).apps/react-admin-example— React 19 + react-admin 5 frontend (port3000) built with Vite (therolldown-vitedrop-in). Includes an experimental RTK Query playground undersrc/resources/rtk-test.
Run from the repo root unless noted.
# Install
yarn
# Start everything: brings up Postgres via Docker, then runs API + frontend in parallel
yarn start
# Bump deps interactively across workspaces
yarn upWorkspace-scoped commands use Yarn workspace syntax, e.g. yarn workspace nestjs-example test.
yarn workspace nestjs-example start # nest start --watch
yarn workspace nestjs-example start:debug # with --debug
yarn workspace nestjs-example build # nest build (clears dist first)
yarn workspace nestjs-example lint
yarn workspace nestjs-example semantic-check # tsc --noemit, full type check
yarn workspace nestjs-example test # jest unit tests (*.spec.ts under src/)
yarn workspace nestjs-example test:e2e # jest using test/jest-e2e.json
yarn workspace nestjs-example test -- path/to/file.spec.ts # single test file
yarn workspace nestjs-example test -- -t "pattern" # filter by name
yarn workspace nestjs-example compodoc # architecture docs at http://localhost:8080TypeORM migrations (the typeorm script builds first, so always run via these wrappers, not raw typeorm):
yarn workspace nestjs-example migration:generate src/database/migrations/<Name>
yarn workspace nestjs-example migration:run
yarn workspace nestjs-example migration:revertdata-source.ts reads apps/nestjs-example/.env (DATABASE_HOST/PORT/USERNAME/PASSWORD/NAME). synchronize: false is intentional — schema changes must go through migrations.
yarn workspace react-admin-example start # vite dev server on :3000
yarn workspace react-admin-example build # tsc + vite build → ../../build/apps/react-admin-example
yarn workspace react-admin-example previewThere is no lint script wired up here, but ESLint is configured (eslint.config.mjs) and can be invoked directly if needed.
yarn workspace postgres start # docker compose up -d
yarn workspace postgres stop # docker compose downThe seeder module is wired in but the README's bootstrap path is to insert an initial user by hand once Postgres is up and migrations have run:
INSERT INTO "public"."user" ("id", "name", "email", "password")
VALUES (1, 'admin', 'admin@gmail.com', 'admin');Passwords are stored and compared as plaintext in AuthService.validateUser — this is example/lab code, not production-grade.
The frontend uses ra-data-simple-rest pointed at http://localhost:3001. That dialect drives several non-obvious conventions on the API side, all of which live in apps/nestjs-example/src/shared/:
- List queries arrive as URL params
filter(JSON object),range(JSON[start, end]), andsort(JSON[field, order]).GetListQuery(shared/models/get-list-query.model.ts) parses and validates them viaclass-transformer+class-validator(SortValidator,FilterValidator). Controllers branch on which fields are present to dispatch togetListvsgetMany. - List responses must expose a
Content-Rangeheader (resource start-end/total).ListPaginationInterceptor(shared/interceptors/list-pagination.interceptor.ts) is applied per-controller (@UseInterceptors) and rewrites the response: it unwraps{ data, contentRange }into a bare array and sets the header, while leaving single-record{ data }responses to return the unwrapped record. New list endpoints must return the sameGetListResult/GetManyResult/GetOneResultshapes for this interceptor to work. - Services return RxJS
Observables (NestJS unwraps them). Controllers composefrom(repo.xxx())withmap/switchMaprather than async/await.
When adding a new resource, mirror tasks/ or users/: a module folder with *.controller.ts, *.service.ts, plus a shared/ subfolder containing entity/, dto/, and services/. Health probes are co-located (*.health.ts) and registered in health/health.module.ts.
apps/nestjs-example/src/authexposes Passportlocal(login) andjwt(request guard) strategies. JWT secret is hardcoded inauth.constant.tsfor the example; access token TTL is short (~45m), refresh token ~1h, and/auth/refreshaccepts the refresh token in the body.- The frontend
authProvider.tsposts credentials to/auth/login, stores the{ access_token, refresh_token }envelope inlocalStorageunder the keyauth, and the globalhttpclient.tsreads that key on every request to setAuthorization: Bearer .... On 401,checkErrorcalls/auth/refreshand replaces the stored token. Anything that constructs requests outsidehttpClient(e.g.authProvideritself) hitshttp://localhost:3001directly. - Controllers are guarded by
JwtAuthGuardat the class level — new controllers should follow suit unless explicitly public.
create-admin-store.ts builds a Redux Toolkit store that combines three RTK Query API slices (usersApi, resourcesApi, healthApi under src/store/api/) and is wrapped around <Admin> via <Provider>. React-admin's own data layer is independent of this store; the RTK slices exist to power the /rtk custom routes and demonstrate patterns like upsertQueryEntries, optimistic updates, and request-batching (usersApi.ts getManyUsers). When touching usersApi, note the bespoke batch() helper that coalesces concurrent getMany calls into one request.
httpclient-adapter.ts adapts the same axios-based httpClient for use as an RTK Query baseQuery, so RTK endpoints and react-admin's dataProvider share auth handling.
vite.config.ts uses vite-tsconfig-paths and the tsconfig has baseUrl: "src" — imports like resources/tasks/... and models/user.model are absolute from src/, not relative.
yarn workspace react-admin-example build writes to the repo-root build/apps/react-admin-example (configured in vite.config.ts), not to a per-app dist/. The NestJS app uses its own apps/nestjs-example/dist/ (required by the typeorm script's entity/migration globs).
- Prettier config (root
.prettierrc): no semicolons, single quotes,printWidth: 120,trailingComma: 'none',arrowParens: 'avoid'. The NestJS workspace overrides this implicitly via its own ESLint/Prettier setup that does emit semicolons insrc/— match the surrounding file. - Imports in NestJS files are grouped with
// Packages,// Models,// Entities,// Dtos,// Services, etc. comments. Keep that grouping when editing. @Controllerclasses applyValidationPipe({ whitelist: true, transform: true })per route via@UsePipes; DTOs lean onclass-validatordecorators andclass-transformer@Transformto coerce JSON-stringified query params.