Sharing pattern: what lives in shared vs frontends/shared
One place for the convention of what is shared across backend and frontends vs frontends-only. Two packages: shared (backend + all frontends; runtime-agnostic) and frontends/shared (frontends only; may use DOM/React).
What belongs where
shared
- API response envelope types and zod schemas (e.g.
api-response). Seeresponses. - DTOs and schemas used by backend validation and frontend forms (create/update, filter/query). See
schemas,requests. - Shared domain types or model shapes. See
models. - Serialization DTOs (e.g. class-transformer shapes). See
serialization. - No Node-only or DOM-only APIs; no NestJS, React, or Vite imports. Keeps the package consumable by both backend and frontends.
frontends/shared
- API client (
handleRequest, axios instance). Seehttp-client,responses. - UI helpers (e.g.
cnfor class names). - Shared React components, frontend-only hooks or utils.
- May depend on
shared(e.g.ApiResponsefromshared/api-response).
Adding and consuming shared code
shared
- Add source under
shared/src/<name>.ts. The package’s subpath exports make it available asshared/<name>without changingpackage.json. Optionally re-export fromshared/src/index.tsfor the root barrel. - Build with
pnpm --filter shared run build(or via rootpnpm build). Seemonorepofor build order and dev watch.
frontends/shared
- Add under
frontends/shared/src/(e.g.src/lib/<name>.ts). Import asfrontends-shared/lib/<name>(or other subpaths per package exports). Subpath pattern; no per-file export entries needed.
Consumption
- Backend and frontends depend on
sharedvia"shared": "workspace:*"; frontends also depend on"frontends-shared": "workspace:*". Consume built output fromdist/; no TypeScript path mapping. Seemonorepoandstatic-typingfor build order and tsconfig.
Implementation
shared: The shared package has a root entry (. → dist/index.js) and a subpath pattern (./* → dist/*.js) in package.json exports. Key files: shared/src/api-response.ts (envelope types and fieldErrorsSchema), shared/src/index.ts (root barrel, e.g. app name and greeting). New modules under shared/src/<name>.ts are importable as shared/<name> without adding export entries.
frontends/shared: The frontends/shared package has root and subpath exports (./*, ./*/*) so modules under frontends/shared/src/ are importable by path (e.g. frontends-shared/lib/api, frontends-shared/lib/utils). The boilerplate provides lib/api.ts (handleRequest, axios instance), lib/utils.ts (cn), and index.ts (e.g. Logo). No new package.json entries needed when adding files under src/.
The generation skill creates both packages and these export shapes; this section documents how the boilerplate implements the sharing pattern.
References
- Stack:
monorepo(workspace layout, build, dev),static-typing(shared tsconfig, consumption). - Patterns:
responses(envelope in shared),requests(validation + shared),schemas(create/update and filter schemas in shared),models(in shared),serialization(shared DTOs).