- Added Option A for using a helper script to build and upload images with tagging and registry push capabilities. - Retained Option B for manual build commands.
26 KiB
📦 Customer Portal - Package Structure & Architecture
This document provides comprehensive information about the Customer Portal codebase structure, package configurations, dependencies, and build architecture.
🏗️ Monorepo Architecture
Complete Project Structure
customer-portal/
├── apps/
│ ├── portal/ # Next.js 15.5 Frontend Application
│ │ ├── src/
│ │ │ ├── app/ # Next.js App Router
│ │ │ ├── components/ # React components
│ │ │ ├── lib/ # Utility functions
│ │ │ └── styles/ # Tailwind CSS styles
│ │ ├── public/ # Static assets
│ │ ├── package.json # Frontend dependencies
│ │ ├── next.config.mjs # Next.js configuration
│ │ ├── tailwind.config.js # Tailwind CSS config
│ │ ├── tsconfig.json # TypeScript config
│ │ └── Dockerfile # Container build file
│ │
│ └── bff/ # NestJS Backend for Frontend
│ ├── src/
│ │ ├── modules/ # Feature modules
│ │ ├── common/ # Shared backend utilities
│ │ ├── config/ # Configuration files
│ │ └── main.ts # Application entry point
│ ├── prisma/
│ │ ├── schema.prisma # Database schema
│ │ ├── migrations/ # Database migrations
│ │ └── seed.ts # Database seeding
│ ├── test/ # Test files
│ ├── package.json # Backend dependencies
│ ├── tsconfig.json # TypeScript config
│ ├── tsconfig.build.json # Build-specific TS config
│ └── Dockerfile # Container build file
│
├── packages/
│ └── shared/ # Shared Package
│ ├── src/
│ │ ├── types/ # TypeScript type definitions
│ │ ├── utils/ # Common utility functions
│ │ ├── constants/ # Application constants
│ │ ├── schemas/ # Validation schemas (Zod)
│ │ └── index.ts # Package exports
│ ├── dist/ # Compiled output (generated)
│ ├── package.json # Shared package config
│ └── tsconfig.json # TypeScript config
│
├── scripts/ # Build and deployment scripts
├── docs/ # Documentation
├── secrets/ # Sensitive configuration files
├── compose-plesk.yaml # Plesk Docker stack
│
├── package.json # Root workspace configuration
├── pnpm-workspace.yaml # pnpm workspace definition
├── pnpm-lock.yaml # Dependency lock file
├── tsconfig.json # Root TypeScript config
├── eslint.config.mjs # ESLint configuration
├── compose-plesk.yaml # Plesk deployment config
├── .env # Environment variables
└── README.md # Project documentation
🛠️ Plesk Deployment Notes (Recommended)
- Proxy rules:
/→ containerportal-frontendport3000/api→ containerportal-backendport4000
- Frontend → Backend base URL: set
NEXT_PUBLIC_API_BASE=/api(same-origin; no CORS needed). - Env split (do not keep secrets under
httpdocs):- Frontend env (client-safe):
/var/www/vhosts/asolutions.jp/private/env/portal-frontend.env - Backend env (server-only):
/var/www/vhosts/asolutions.jp/private/env/portal-backend.env
- Frontend env (client-safe):
- Compose (
compose-plesk.yaml) highlights:- Binds app ports to
127.0.0.1and keeps DB/Redis internal. - Uses
env_fileper service pointing to the private env files above. - Backend waits for Postgres then runs migrations.
- Binds app ports to
- Alpine images: backend installs toolchain for bcrypt/Prisma; frontend includes
libc6-compat. - Health endpoints: Next
/api/health, Nest/health.
Environment Files (Plesk)
- Create two env files on the server (under the domain's private dir):
- Frontend:
/var/www/vhosts/asolutions.jp/private/env/portal-frontend.env— based onenv/portal-frontend.env.sample- Must include:
NEXT_PUBLIC_API_BASE=/api
- Must include:
- Backend:
/var/www/vhosts/asolutions.jp/private/env/portal-backend.env— based onenv/portal-backend.env.sample- Must include:
TRUST_PROXY=true DATABASE_URLshould usedatabase:5432REDIS_URLshould usecache:6379- Set
JWT_SECRETto a strong value
- Must include:
- Frontend:
Image Build and Upload
Option A — Use the helper script (recommended):
# Build both images, tag :latest and a date+sha tag, and write tarballs
scripts/plesk/build-images.sh
# Custom tag and output directory
scripts/plesk/build-images.sh --tag v1.0.0 --output ./dist
# Also push to a registry (e.g., GHCR)
scripts/plesk/build-images.sh --tag v1.0.0 --push ghcr.io/<org>
Option B — Manual build commands:
# Frontend
docker build -t portal-frontend:latest -f apps/portal/Dockerfile .
docker save -o portal-frontend.latest.tar portal-frontend:latest
# Backend
docker build -t portal-backend:latest -f apps/bff/Dockerfile .
docker save -o portal-backend.latest.tar portal-backend:latest
In Plesk → Docker → Images, upload both tar files. Then use compose-plesk.yaml under Docker → Stacks → Add Stack to deploy the services. Configure Proxy Rules on the domain:
/→portal-frontendport3000/api→portal-backendport4000
Alternatively, load via SSH on the Plesk host:
scp portal-frontend.latest.tar portal-backend.latest.tar user@plesk-host:/tmp/
ssh user@plesk-host
sudo docker load -i /tmp/portal-frontend.latest.tar
sudo docker load -i /tmp/portal-backend.latest.tar
Or push to a registry (example: GHCR):
docker tag portal-frontend:latest ghcr.io/<org>/portal-frontend:latest
docker tag portal-backend:latest ghcr.io/<org>/portal-backend:latest
docker push ghcr.io/<org>/portal-frontend:latest
docker push ghcr.io/<org>/portal-backend:latest
Quick checklist:
- Proxy rules added for
/and/api. NEXT_PUBLIC_API_BASE=/apiavailable to the frontend.- DB URL uses
database:5432(compose service name). - Secrets are under
/var/www/vhosts/asolutions.jp/private(nothttpdocs).
Technology Stack & Versions
- Runtime: Node.js 22+ (LTS)
- Package Manager: pnpm 10.15.0 (workspace support)
- Language: TypeScript 5.9.2 (strict mode)
- Frontend Framework: Next.js 15.5.0 with React 19.1.1
- Backend Framework: NestJS 11.1.6
- Database ORM: Prisma 6.14.0
- Styling: Tailwind CSS 4.1.12
- State Management: Zustand 5.0.8 + TanStack Query 5.85.5
- Validation: Zod 4.0.17
- Authentication: JWT + bcrypt
- Database: PostgreSQL 17
- Cache: Redis 7 with ioredis 5.7.0
📦 Workspace Configuration
Root Package.json (Workspace Root)
{
"name": "customer-portal",
"version": "1.0.0",
"description": "Customer portal with BFF architecture",
"private": true,
"packageManager": "pnpm@10.15.0",
"engines": {
"node": ">=22.0.0",
"pnpm": ">=10.0.0"
},
"scripts": {
"predev": "pnpm --filter @customer-portal/shared build",
"dev": "./scripts/dev/manage.sh apps",
"dev:all": "pnpm --parallel --filter @customer-portal/shared --filter @customer-portal/portal --filter @customer-portal/bff run dev",
"build": "pnpm --recursive --reporter=default run build",
"start": "pnpm --parallel --filter @customer-portal/portal --filter @customer-portal/bff run start",
"test": "pnpm --recursive run test",
"lint": "pnpm --recursive run lint",
"type-check": "pnpm --filter @customer-portal/shared build && pnpm --recursive run type-check",
"clean": "pnpm --recursive run clean"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.34.0",
"@types/node": "^24.3.0",
"eslint": "^9.33.0",
"eslint-config-next": "15.5.0",
"eslint-plugin-prettier": "^5.5.4",
"globals": "^16.3.0",
"husky": "^9.1.7",
"prettier": "^3.6.2",
"typescript": "^5.9.2",
"typescript-eslint": "^8.40.0"
},
"dependencies": {
"@sendgrid/mail": "^8.1.5"
}
}
Workspace Definition (pnpm-workspace.yaml)
packages:
- 'apps/*' # Frontend and Backend applications
- 'packages/*' # Shared libraries and utilities
Package Dependency Graph
@customer-portal/shared (packages/shared)
├── Built first (no dependencies)
└── Exports: types, utils, constants, schemas
@customer-portal/portal (apps/portal)
├── Depends on: @customer-portal/shared
└── Builds: Next.js standalone application
@customer-portal/bff (apps/bff)
├── Depends on: @customer-portal/shared
├── Generates: Prisma client
└── Builds: NestJS application
🎯 Frontend Application (apps/portal)
Complete Package Configuration
{
"name": "@customer-portal/portal",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p ${NEXT_PORT:-3000}",
"build": "next build",
"build:turbo": "next build --turbopack",
"start": "next start -p ${NEXT_PORT:-3000}",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"type-check": "tsc --noEmit",
"test": "echo 'No tests yet'"
},
"dependencies": {
"@customer-portal/shared": "workspace:*",
"@heroicons/react": "^2.2.0",
"@hookform/resolvers": "^5.2.1",
"@tanstack/react-query": "^5.85.5",
"@tanstack/react-query-devtools": "^5.85.5",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"lucide-react": "^0.540.0",
"next": "15.5.0",
"react": "19.1.1",
"react-dom": "19.1.1",
"react-hook-form": "^7.62.0",
"tailwind-merge": "^3.3.1",
"tw-animate-css": "^1.3.7",
"world-countries": "^5.1.0",
"zod": "^4.0.17",
"zustand": "^5.0.8"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.12",
"@types/node": "^24.3.0",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"tailwindcss": "^4.1.12",
"typescript": "^5.9.2"
}
}
Frontend Architecture & Dependencies
Core Framework Stack
- Next.js 15.5.0: App Router, Server Components, Streaming
- React 19.1.1: Latest React with concurrent features
- TypeScript 5.9.2: Strict type checking
UI & Styling
- Tailwind CSS 4.1.12: Utility-first CSS framework
- @tailwindcss/postcss: PostCSS integration
- tailwind-merge: Conditional class merging
- class-variance-authority: Component variant management
- clsx: Conditional className utility
- tw-animate-css: Tailwind animation utilities
State Management & Data Fetching
- Zustand 5.0.8: Lightweight state management
- TanStack Query 5.85.5: Server state management
- @tanstack/react-query-devtools: Development tools
Forms & Validation
- react-hook-form 7.62.0: Form state management
- @hookform/resolvers 5.2.1: Validation resolvers
- Zod 4.0.17: Runtime type validation
Icons & UI Components
- @heroicons/react 2.2.0: SVG icon library
- lucide-react 0.540.0: Additional icon set
Utilities
- date-fns 4.1.0: Date manipulation library
- world-countries 5.1.0: Country data
Next.js Configuration (next.config.mjs)
const nextConfig = {
// Enable standalone output for production deployment
output: process.env.NODE_ENV === "production" ? "standalone" : undefined,
// Exclude server-only packages from client bundle
serverExternalPackages: [
"pino", "pino-pretty", "pino-abstract-transport",
"thread-stream", "sonic-boom"
],
// Turbopack configuration (Next.js 15.5+)
turbopack: {
resolveAlias: { "@": "./src" }
},
// Environment variables validation
env: {
NEXT_PUBLIC_API_BASE: process.env.NEXT_PUBLIC_API_BASE,
NEXT_PUBLIC_APP_NAME: process.env.NEXT_PUBLIC_APP_NAME,
NEXT_PUBLIC_APP_VERSION: process.env.NEXT_PUBLIC_APP_VERSION,
},
// Image optimization
images: {
remotePatterns: [{ protocol: "https", hostname: "**" }]
},
// Disable ESLint during builds (handled separately)
eslint: { ignoreDuringBuilds: true },
// Security headers
async headers() {
return [{
source: "/(.*)",
headers: [
{ key: "X-Frame-Options", value: "DENY" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }
]
}];
},
// Production optimizations
compiler: {
removeConsole: process.env.NODE_ENV === "production"
}
};
TypeScript Configuration (tsconfig.json)
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"plugins": [{ "name": "next" }]
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": ["node_modules"]
}
🎯 Backend Application (apps/bff)
Complete Package Configuration
{
"name": "@customer-portal/bff",
"version": "1.0.0",
"description": "Backend for Frontend API",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build -c tsconfig.build.json",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"dev": "NODE_OPTIONS=\"--no-deprecation\" nest start --watch --preserveWatchOutput -c tsconfig.build.json",
"start:debug": "NODE_OPTIONS=\"--no-deprecation\" nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"type-check": "tsc --noEmit",
"clean": "rm -rf dist",
"db:migrate": "prisma migrate dev",
"db:generate": "prisma generate",
"db:studio": "prisma studio",
"db:reset": "prisma migrate reset",
"db:seed": "ts-node prisma/seed.ts"
},
"dependencies": {
"@customer-portal/shared": "workspace:*",
"@nestjs/bullmq": "^11.0.3",
"@nestjs/common": "^11.1.6",
"@nestjs/config": "^4.0.2",
"@nestjs/core": "^11.1.6",
"@nestjs/jwt": "^11.0.0",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.1.6",
"@nestjs/swagger": "^11.2.0",
"@nestjs/throttler": "^6.4.0",
"@prisma/client": "^6.14.0",
"@types/jsonwebtoken": "^9.0.10",
"bcrypt": "^6.0.0",
"bullmq": "^5.58.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"cookie-parser": "^1.4.7",
"helmet": "^8.1.0",
"ioredis": "^5.7.0",
"jsforce": "^3.10.4",
"jsonwebtoken": "^9.0.2",
"nestjs-pino": "^4.4.0",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"pino": "^9.9.0",
"pino-http": "^10.5.0",
"pino-pretty": "^13.1.1",
"prisma": "^6.14.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.2",
"@sendgrid/mail": "^8.1.3",
"speakeasy": "^2.0.0",
"uuid": "^11.1.0",
"zod": "^4.0.17"
},
"devDependencies": {
"@nestjs/cli": "^11.0.10",
"@nestjs/schematics": "^11.0.7",
"@nestjs/testing": "^11.1.6",
"@types/bcrypt": "^6.0.0",
"@types/cookie-parser": "^1.4.9",
"@types/express": "^5.0.3",
"@types/jest": "^30.0.0",
"@types/node": "^24.3.0",
"@types/passport-jwt": "^4.0.1",
"@types/passport-local": "^1.0.38",
"@types/speakeasy": "^2.0.10",
"@types/supertest": "^6.0.3",
"@types/uuid": "^10.0.0",
"jest": "^30.0.5",
"source-map-support": "^0.5.21",
"supertest": "^7.1.4",
"ts-jest": "^29.4.1",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.9.2"
}
}
Backend Architecture & Dependencies
Core NestJS Framework
- @nestjs/core 11.1.6: Core framework
- @nestjs/common 11.1.6: Common decorators and utilities
- @nestjs/platform-express 11.1.6: Express platform adapter
- @nestjs/config 4.0.2: Configuration management
- reflect-metadata 0.2.2: Metadata reflection API
Authentication & Security
- @nestjs/passport 11.0.5: Passport integration
- @nestjs/jwt 11.0.0: JWT token handling
- passport 0.7.0: Authentication middleware
- passport-jwt 4.0.1: JWT strategy
- passport-local 1.0.0: Local authentication strategy
- jsonwebtoken 9.0.2: JWT implementation
- bcrypt 6.0.0: Password hashing
- helmet 8.1.0: Security headers
- @nestjs/throttler 6.4.0: Rate limiting
- speakeasy 2.0.0: Two-factor authentication
Database & ORM
- @prisma/client 6.14.0: Prisma ORM client
- prisma 6.14.0: Prisma CLI and schema management
Caching & Queues
- ioredis 5.7.0: Redis client
- @nestjs/bullmq 11.0.3: Queue management
- bullmq 5.58.0: Redis-based queue system
Validation & Transformation
- class-validator 0.14.2: Decorator-based validation
- class-transformer 0.5.1: Object transformation
- zod 4.0.17: Runtime type validation
External Integrations
- jsforce 3.10.4: Salesforce API client
- @sendgrid/mail 8.1.3: SendGrid email service
Logging & Monitoring
- nestjs-pino 4.4.0: NestJS Pino integration
- pino 9.9.0: High-performance logging
- pino-http 10.5.0: HTTP request logging
- pino-pretty 13.1.1: Pretty-printed logs for development
API Documentation
- @nestjs/swagger 11.2.0: OpenAPI/Swagger documentation
Utilities
- rxjs 7.8.2: Reactive programming
- uuid 11.1.0: UUID generation
- cookie-parser 1.4.7: Cookie parsing middleware
TypeScript Configurations
Main TypeScript Config (tsconfig.json)
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
},
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"lib": ["ES2021"],
"module": "commonjs",
"declaration": true,
"removeComments": true,
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
Build TypeScript Config (tsconfig.build.json)
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
Jest Testing Configuration
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s",
"!**/*.spec.ts",
"!**/node_modules/**"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node",
"moduleNameMapping": {
"^@/(.*)$": "<rootDir>/$1"
},
"passWithNoTests": true
}
📦 Shared Package (packages/shared)
Complete Package Configuration
{
"name": "@customer-portal/shared",
"version": "1.0.0",
"description": "Shared utilities and types for Customer Portal",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"private": true,
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"clean": "rm -rf dist",
"type-check": "tsc --noEmit"
},
"dependencies": {
"zod": "^4.0.17"
},
"devDependencies": {
"typescript": "^5.9.2",
"@types/node": "^24.3.0"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
}
TypeScript Configuration (tsconfig.json)
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"composite": true,
"incremental": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Package Structure & Exports
// src/index.ts - Main export file
export * from './types';
export * from './utils';
export * from './constants';
export * from './schemas';
// src/types/index.ts - Type definitions
export interface User {
id: string;
email: string;
name: string;
role: UserRole;
}
export type UserRole = 'admin' | 'customer' | 'support';
export interface ApiResponse<T = any> {
success: boolean;
data?: T;
error?: string;
message?: string;
}
// src/utils/index.ts - Utility functions
export const formatCurrency = (amount: number, currency = 'USD') => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency
}).format(amount);
};
export const slugify = (text: string): string => {
return text
.toLowerCase()
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-')
.replace(/^-+|-+$/g, '');
};
// src/constants/index.ts - Application constants
export const API_ENDPOINTS = {
AUTH: '/auth',
USERS: '/users',
PRODUCTS: '/products',
ORDERS: '/orders'
} as const;
export const USER_ROLES = {
ADMIN: 'admin',
CUSTOMER: 'customer',
SUPPORT: 'support'
} as const;
// src/schemas/index.ts - Zod validation schemas
import { z } from 'zod';
export const userSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1),
role: z.enum(['admin', 'customer', 'support'])
});
export const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(8)
});
Build Output Structure
packages/shared/dist/
├── index.js # Main compiled entry point
├── index.d.ts # Type definitions
├── index.js.map # Source map
├── types/
│ ├── index.js
│ └── index.d.ts
├── utils/
│ ├── index.js
│ └── index.d.ts
├── constants/
│ ├── index.js
│ └── index.d.ts
└── schemas/
├── index.js
└── index.d.ts
🔨 Build Process & Dependencies
Complete Build Order
# 1. Install all workspace dependencies
pnpm install --frozen-lockfile
# 2. Build shared package (required by other packages)
cd packages/shared
pnpm build
# 3. Generate Prisma client (backend requirement)
cd ../../apps/bff
pnpm prisma generate
# 4. Build backend application
pnpm build
# 5. Build frontend application
cd ../portal
pnpm build
Production Build Requirements
Shared Package Build
- Input: TypeScript source files in
src/ - Output: Compiled JavaScript + type definitions in
dist/ - Dependencies: Only Zod for runtime validation
- Build time: ~5-10 seconds
Frontend Build
- Input: Next.js application with React components
- Output: Standalone server bundle + static assets
- Dependencies: Shared package must be built first
- Build time: ~30-60 seconds
- Output size: ~15-25MB (standalone bundle)
Backend Build
- Input: NestJS application with Prisma schema
- Output: Compiled Node.js application
- Dependencies: Shared package + generated Prisma client
- Build time: ~20-40 seconds
- Critical step: Prisma client generation before TypeScript compilation
Native Module Compilation
These packages require native compilation and rebuilding in production:
bcrypt (Password Hashing)
- Platform dependent: Yes (native C++ bindings)
- Rebuild required: Yes, for target architecture
- Build command:
pnpm rebuild bcrypt
@prisma/client (Database ORM)
- Platform dependent: Yes (query engine binaries)
- Rebuild required: Yes, includes platform-specific engines
- Build command:
pnpm rebuild @prisma/client @prisma/engines
@prisma/engines
- Platform dependent: Yes (database query engines)
- Architecture specific: linux-musl-x64, linux-gnu-x64, etc.
- Size: ~50-100MB of engine binaries
Environment-Specific Configurations
Development Environment
- Hot reload: Enabled for all packages
- Source maps: Generated for debugging
- Type checking: Strict mode enabled
- Logging: Pretty-printed with colors
Production Environment
- Minification: Enabled for frontend
- Source maps: Disabled for security
- Console removal: Automatic in frontend builds
- Logging: JSON format for structured logging
- Health checks: Built-in endpoints for monitoring
📋 Package Summary
Dependency Overview
Total packages: 3 workspace packages
├── @customer-portal/shared (packages/shared)
│ ├── Dependencies: 1 (zod)
│ ├── DevDependencies: 2 (typescript, @types/node)
│ └── Build output: ~50KB
│
├── @customer-portal/portal (apps/portal)
│ ├── Dependencies: 16 (Next.js, React, UI libraries)
│ ├── DevDependencies: 5 (Tailwind, TypeScript types)
│ └── Build output: ~15-25MB (standalone)
│
└── @customer-portal/bff (apps/bff)
├── Dependencies: 25 (NestJS, Prisma, integrations)
├── DevDependencies: 13 (testing, build tools)
└── Build output: ~5-10MB
Critical Build Dependencies
- pnpm 10.15.0: Workspace package manager
- TypeScript 5.9.2: Language compiler for all packages
- Node.js 22+: Runtime environment
- Prisma 6.14.0: Database client generation
- Next.js 15.5.0: Frontend framework with standalone output
Package Interdependencies
Build Order (Critical):
1. packages/shared → Compiles TypeScript to JavaScript
2. apps/bff → Generates Prisma client, then compiles NestJS
3. apps/portal → Compiles Next.js with shared package dependency
Runtime Dependencies:
- Frontend depends on shared package types and utilities
- Backend depends on shared package + generated Prisma client
- Both applications require external services (PostgreSQL, Redis)
Key Configuration Files
- pnpm-workspace.yaml: Defines monorepo structure
- Root package.json: Workspace scripts and shared devDependencies
- tsconfig.json: TypeScript configuration inheritance
- next.config.mjs: Next.js production optimizations
- prisma/schema.prisma: Database schema and client generation
This architecture provides a scalable, type-safe monorepo with shared utilities, modern frontend framework, robust backend API, and comprehensive external service integrations.