Phase 00: Empty Shell
Three applications that start and connect - monorepo, Next.js, NestJS, Prisma, Docker
Phase 00: Empty Shell
Goal: Three applications that start and connect. Nothing works yet except "hello world" level responses.
| Attribute | Value |
|---|---|
| Steps | 01-08 |
| Estimated Time | 4-6 hours |
| Dependencies | Empty directory or new git repo |
| Completion Gate | All three services running, health endpoint returns JSON with database connection status |
Phase Context (READ FIRST)
What This Phase Accomplishes
- Three applications that start and connect
- Health endpoint verifies database connection
- NOTHING ELSE
What This Phase Does NOT Include
- Authentication (Phase 01)
- Multi-tenancy (Phase 01)
- Any business logic
- Any UI beyond "hello world"
- Any API beyond health check
Bluewoo Anti-Pattern Reminder
This phase intentionally has NO:
- Shared libraries between apps (just workspace reference)
- GraphQL (REST only)
- Event-driven architecture
- Caching layers
- Microservices patterns
If the AI suggests adding any of these, REJECT and continue with the spec.
Step 01: Create Monorepo Structure
Input
- Empty directory or new git repo
- Node.js 20+ installed
- npm 10+ (comes with Node.js)
Constraints
- DO NOT add any dependencies beyond typescript
- DO NOT create any additional directories
- DO NOT modify the workspace structure after creation
- ONLY create:
package.json,.gitignore,.nvmrc
Task
# Create project directory
mkdir hrms && cd hrms
# Initialize git
git init
# Create directory structure
mkdir -p apps/web apps/api packages/database
# Create root package.json with npm workspaces
cat > package.json << 'EOF'
{
"name": "hrms",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"dev": "npm run dev --workspaces --if-present",
"build": "npm run build --workspaces --if-present",
"lint": "npm run lint --workspaces --if-present",
"test": "npm test --workspaces --if-present"
},
"devDependencies": {
"typescript": "^5.7.3"
},
"engines": {
"node": ">=20.0.0"
}
}
EOF
# Create .gitignore
cat > .gitignore << 'EOF'
node_modules
.env
.env.local
dist
.next
.turbo
*.log
.DS_Store
EOF
# Create .nvmrc
echo "20" > .nvmrc
# Install dependencies
npm installGate
ls apps/web apps/api packages/database
# Should show all three folders exist
cat package.json
# Should show workspaces configurationCommon Errors
| Error | Cause | Fix |
|---|---|---|
npm ERR! code ENOWORKSPACES | Workspaces not configured | Ensure package.json has "workspaces" field |
ENOENT: no such file or directory | Wrong directory | Ensure you're in the hrms root directory |
Rollback
# If Gate fails, start over
cd ..
rm -rf hrmsLock
After this step, these files are locked (do not modify without ADR):
package.json(workspaces configuration)
Checkpoint
Before proceeding to Step 02:
-
ls apps/web apps/api packages/databaseshows all folders -
cat package.jsonshows workspaces config - No errors in console
- Type "GATE 01 PASSED" to continue
Step 02: Create Next.js App Shell
Input
- Step 01 complete
- Monorepo structure exists
Constraints
- DO NOT add any UI libraries (Tailwind, shadcn/ui come in Phase 01)
- DO NOT add any routing beyond the root page
- DO NOT modify files from Step 01
- ONLY create files in
apps/web/
Task
cd apps/web
# Create package.json
cat > package.json << 'EOF'
{
"name": "@hrms/web",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "15.3.4",
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"@types/node": "^22.15.29",
"@types/react": "^19.1.6",
"@types/react-dom": "^19.1.5",
"typescript": "^5.7.3"
}
}
EOF
# Create next.config.ts
cat > next.config.ts << 'EOF'
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
reactStrictMode: true,
}
export default nextConfig
EOF
# Create tsconfig.json
cat > tsconfig.json << 'EOF'
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [{ "name": "next" }],
"paths": { "@/*": ["./*"] }
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
EOF
# Create app directory structure
mkdir -p app
# Create app/layout.tsx
cat > app/layout.tsx << 'EOF'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'HRMS',
description: 'Human Resource Management System',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
EOF
# Create app/page.tsx
cat > app/page.tsx << 'EOF'
export default function Home() {
return (
<main style={{ padding: '2rem' }}>
<h1>HRMS</h1>
<p>Human Resource Management System</p>
</main>
)
}
EOF
# Go back to root and install
cd ../..
npm installGate
cd apps/web && npm run dev
# Open browser to http://localhost:3000
# Should show "HRMS" heading and "Human Resource Management System" text
# Press Ctrl+C to stopCommon Errors
| Error | Cause | Fix |
|---|---|---|
Module not found: react | Dependencies not installed | Run npm install from root |
Port 3000 already in use | Another process using port | Kill process: lsof -ti:3000 | xargs kill |
next: command not found | Not in apps/web directory | cd apps/web first |
Rollback
# Undo Step 02
rm -rf apps/web/*Lock
After this step, these files are locked:
apps/web/package.jsonapps/web/next.config.ts
Checkpoint
Before proceeding to Step 03:
-
npm run devshows Next.js page at localhost:3000 - Page shows "HRMS" heading
- No console errors
- Type "GATE 02 PASSED" to continue
Step 03: Create NestJS App Shell
Input
- Step 02 complete
- Next.js app runs on port 3000
Constraints
- DO NOT add any middleware or guards
- DO NOT add any additional endpoints
- DO NOT add database connection (that's Step 07)
- DO NOT modify files from Steps 01-02
- ONLY create files in
apps/api/
Task
cd apps/api
# Create package.json
cat > package.json << 'EOF'
{
"name": "@hrms/api",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "nest start --watch",
"build": "nest build",
"start": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\"",
"test": "jest"
},
"dependencies": {
"@nestjs/common": "^10.5.0",
"@nestjs/core": "^10.5.0",
"@nestjs/platform-express": "^10.5.0",
"@nestjs/throttler": "^5.0.0",
"helmet": "^7.1.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@nestjs/cli": "^10.5.0",
"@nestjs/schematics": "^10.2.4",
"@nestjs/testing": "^10.5.0",
"@types/express": "^5.0.1",
"@types/node": "^22.15.29",
"typescript": "^5.7.3"
}
}
EOF
# Create nest-cli.json
cat > nest-cli.json << 'EOF'
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
EOF
# Create tsconfig.json
cat > tsconfig.json << 'EOF'
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": true,
"noImplicitAny": true,
"strictBindCallApply": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true
}
}
EOF
# Create src directory
mkdir -p src
# Create src/main.ts
cat > src/main.ts << 'EOF'
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import helmet from 'helmet';
import { AppModule } from './app.module';
import { GlobalExceptionFilter } from './common/filters/global-exception.filter';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Security headers (XSS, clickjacking, MIME sniffing protection)
app.use(helmet());
// CORS configuration
app.enableCors({
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
credentials: true,
});
// Global validation pipe (whitelist strips unknown properties)
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
// Global exception filter (handles Prisma errors, prevents stack trace leaks)
app.useGlobalFilters(new GlobalExceptionFilter());
await app.listen(3001);
console.log('HRMS API running on http://localhost:3001');
}
bootstrap();
EOF
# Create src/app.module.ts
cat > src/app.module.ts << 'EOF'
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { AppController } from './app.controller';
@Module({
imports: [
// Rate limiting: 100 requests per minute per IP
ThrottlerModule.forRoot([{
ttl: 60000,
limit: 100,
}]),
],
controllers: [AppController],
providers: [
{ provide: APP_GUARD, useClass: ThrottlerGuard },
],
})
export class AppModule {}
EOF
# Create common/filters directory
mkdir -p src/common/filters
# Create src/common/filters/global-exception.filter.ts
cat > src/common/filters/global-exception.filter.ts << 'EOF'
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
Logger,
} from '@nestjs/common';
import { Response } from 'express';
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(GlobalExceptionFilter.name);
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
let status = HttpStatus.INTERNAL_SERVER_ERROR;
let message = 'Internal server error';
if (exception instanceof HttpException) {
status = exception.getStatus();
const exceptionResponse = exception.getResponse();
message = typeof exceptionResponse === 'string'
? exceptionResponse
: (exceptionResponse as any).message || exception.message;
}
// Log unexpected errors (don't leak stack traces to client)
if (status === HttpStatus.INTERNAL_SERVER_ERROR) {
this.logger.error('Unhandled exception', exception);
}
response.status(status).json({
data: null,
error: { message, statusCode: status },
});
}
}
EOF
# Create src/app.controller.ts
cat > src/app.controller.ts << 'EOF'
import { Controller, Get } from '@nestjs/common';
@Controller()
export class AppController {
@Get()
getHello(): string {
return 'HRMS API Running';
}
}
EOF
# Go back to root and install
cd ../..
npm installGate
cd apps/api && npm run dev
# Wait for "HRMS API running on http://localhost:3001"
# In another terminal:
curl http://localhost:3001
# Should return: HRMS API RunningCommon Errors
| Error | Cause | Fix |
|---|---|---|
Cannot find module '@nestjs/core' | Dependencies not installed | Run npm install from root |
Port 3001 already in use | Another process using port | Kill process: lsof -ti:3001 | xargs kill |
nest: command not found | NestJS CLI not available | Dependencies should be local, check package.json |
Rollback
# Undo Step 03
rm -rf apps/api/*Lock
After this step, these files are locked:
apps/api/nest-cli.jsonapps/api/tsconfig.json
Checkpoint
Before proceeding to Step 04:
-
curl localhost:3001returns "HRMS API Running" - Console shows "HRMS API running on http://localhost:3001"
- No errors in logs
- Type "GATE 03 PASSED" to continue
Step 04: Create Database Package with Prisma
Input
- Step 03 complete
- NestJS app runs on port 3001
Constraints
- DO NOT add any models beyond HealthCheck
- DO NOT run prisma db push yet (that's Step 06)
- DO NOT connect to database yet
- DO NOT modify files from Steps 01-03
- ONLY create files in
packages/database/ - USE Prisma 5.x (NOT 6.x)
Task
cd packages/database
# Create package.json
cat > package.json << 'EOF'
{
"name": "@hrms/database",
"version": "0.1.0",
"private": true,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"db:generate": "prisma generate",
"db:push": "prisma db push",
"db:studio": "prisma studio",
"db:migrate": "prisma migrate dev"
},
"dependencies": {
"@prisma/client": "^5.22.0"
},
"devDependencies": {
"prisma": "^5.22.0",
"typescript": "^5.7.3"
}
}
EOF
# Create tsconfig.json
cat > tsconfig.json << 'EOF'
{
"compilerOptions": {
"target": "ES2021",
"module": "commonjs",
"lib": ["ES2021"],
"declaration": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
EOF
# Create prisma directory
mkdir -p prisma
# Create prisma/schema.prisma
cat > prisma/schema.prisma << 'EOF'
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// Minimal model for Phase 00 - just to verify connection
model HealthCheck {
id String @id @default(uuid())
status String
checkedAt DateTime @default(now())
}
EOF
# Create src directory
mkdir -p src
# Create src/index.ts
cat > src/index.ts << 'EOF'
import { PrismaClient } from '@prisma/client';
export const prisma = new PrismaClient();
export { PrismaClient };
export * from '@prisma/client';
EOF
# Create .env.example
cat > .env.example << 'EOF'
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/hrms?schema=public"
EOF
# Go back to root and install
cd ../..
npm installGate
cat packages/database/prisma/schema.prisma
# Should show HealthCheck model with id, status, checkedAt fieldsCommon Errors
| Error | Cause | Fix |
|---|---|---|
Cannot find module 'prisma' | Prisma not installed | Run npm install from root |
Prisma schema validation error | Schema syntax error | Check schema.prisma for typos |
Rollback
# Undo Step 04
rm -rf packages/database/*Lock
After this step, these files are locked:
packages/database/package.json
Checkpoint
Before proceeding to Step 05:
-
cat packages/database/prisma/schema.prismashows HealthCheck model - Schema has id, status, checkedAt fields
- No syntax errors
- Type "GATE 04 PASSED" to continue
Step 05: Create Docker Compose for PostgreSQL
Input
- Step 04 complete
- Docker installed and running
Constraints
- DO NOT add any services beyond PostgreSQL
- DO NOT add Redis, MongoDB, or other databases
- DO NOT modify files from Steps 01-04
- ONLY create:
docker-compose.ymlin project root - USE PostgreSQL 17 (NOT older versions)
Task
# In project root, create docker-compose.yml
cat > docker-compose.yml << 'EOF'
services:
postgres:
image: postgres:17
container_name: hrms-postgres
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: hrms
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
EOFGate
docker compose up -d
docker compose ps
# Should show hrms-postgres running and healthy
# Test connection
docker exec hrms-postgres pg_isready -U postgres
# Should return: /var/run/postgresql:5432 - accepting connectionsCommon Errors
| Error | Cause | Fix |
|---|---|---|
Cannot connect to Docker daemon | Docker not running | Start Docker Desktop |
port is already allocated | Port 5432 in use | Stop other PostgreSQL: docker stop $(docker ps -q) |
image not found | Network issue | Check internet connection, retry docker compose up |
Rollback
# Undo Step 05
docker compose down -v
rm docker-compose.ymlLock
After this step, these files are locked:
docker-compose.yml
Checkpoint
Before proceeding to Step 06:
-
docker compose psshows hrms-postgres running -
docker exec hrms-postgres pg_isready -U postgresshows accepting connections - No errors in Docker logs
- Type "GATE 05 PASSED" to continue
Step 06: Connect Prisma to PostgreSQL
Input
- Step 05 complete
- PostgreSQL running in Docker
Constraints
- DO NOT modify the schema (just push existing schema)
- DO NOT add migrations yet (use db push for now)
- DO NOT modify files from Steps 01-05
- ONLY create:
.envfile in packages/database/
Task
cd packages/database
# Create .env file (copy from example)
cp .env.example .env
# Push schema to database
npm run db:push
# Generate Prisma client
npm run db:generate
cd ../..Gate
cd packages/database && npm run db:studio
# Should open Prisma Studio in browser
# Should show HealthCheck table (empty)
# Press Ctrl+C to stopCommon Errors
| Error | Cause | Fix |
|---|---|---|
P1001: Can't reach database | PostgreSQL not running | Run docker compose up -d |
P1000: Authentication failed | Wrong credentials | Check DATABASE_URL matches docker-compose.yml |
Error: ENOENT .env | .env file missing | Run cp .env.example .env |
Rollback
# Undo Step 06
cd packages/database
rm .env
# Note: db:push created a table, but it's okay to leave itLock
After this step, these files are locked:
packages/database/.env.example
Checkpoint
Before proceeding to Step 07:
-
npm run db:studioopens Prisma Studio - HealthCheck table visible (empty is OK)
- No connection errors
- Type "GATE 06 PASSED" to continue
Step 07: Connect API to Database
Input
- Step 06 complete
- Prisma can connect to PostgreSQL
Constraints
- DO NOT add any business logic
- DO NOT add any endpoints (that's Step 08)
- DO NOT modify Prisma schema
- DO NOT modify files from Steps 01-06 (except apps/api/package.json)
- ONLY create files in
apps/api/src/prisma/
Task
cd apps/api
# Add database package as dependency
cat > package.json << 'EOF'
{
"name": "@hrms/api",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "nest start --watch",
"build": "nest build",
"start": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\"",
"test": "jest"
},
"dependencies": {
"@hrms/database": "workspace:*",
"@nestjs/common": "^10.5.0",
"@nestjs/core": "^10.5.0",
"@nestjs/platform-express": "^10.5.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@nestjs/cli": "^10.5.0",
"@nestjs/schematics": "^10.2.4",
"@nestjs/testing": "^10.5.0",
"@types/express": "^5.0.1",
"@types/node": "^22.15.29",
"typescript": "^5.7.3"
}
}
EOF
# Create prisma module directory
mkdir -p src/prisma
# Create src/prisma/prisma.module.ts
cat > src/prisma/prisma.module.ts << 'EOF'
import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
EOF
# Create src/prisma/prisma.service.ts
cat > src/prisma/prisma.service.ts << 'EOF'
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@hrms/database';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}
EOF
# Create src/prisma/index.ts
cat > src/prisma/index.ts << 'EOF'
export * from './prisma.module';
export * from './prisma.service';
EOF
# Update app.module.ts to import PrismaModule
cat > src/app.module.ts << 'EOF'
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { PrismaModule } from './prisma';
@Module({
imports: [PrismaModule],
controllers: [AppController],
providers: [],
})
export class AppModule {}
EOF
# Create .env file for API
cat > .env << 'EOF'
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/hrms?schema=public"
EOF
# Go back to root and install
cd ../..
npm installGate
cd apps/api && npm run dev
# Watch logs - should NOT see database connection errors
# Should see "HRMS API running on http://localhost:3001"Common Errors
| Error | Cause | Fix |
|---|---|---|
Cannot find module '@hrms/database' | npm install not run | Run npm install from root |
PrismaClient is not defined | Prisma client not generated | Run cd packages/database && npm run db:generate |
Connection refused | PostgreSQL not running | Run docker compose up -d |
Cannot find module './prisma' | Import path wrong | Check import { PrismaModule } from './prisma' |
Nest can't resolve dependencies | PrismaModule not imported | Verify PrismaModule in AppModule imports |
Rollback
# Undo Step 07
rm -rf apps/api/src/prisma
rm apps/api/.env
git checkout -- apps/api/src/app.module.ts
git checkout -- apps/api/package.jsonLock
After this step, these files are locked:
apps/api/src/prisma/*
Checkpoint
Before proceeding to Step 08:
- API starts without errors
- Console shows "HRMS API running on http://localhost:3001"
- No database connection errors in logs
- Type "GATE 07 PASSED" to continue
Step 08: Create Health Endpoint
Input
- Step 07 complete
- API connects to database without errors
Constraints
- DO NOT add any business logic beyond health check
- DO NOT add authentication or guards
- DO NOT add additional endpoints
- DO NOT modify files from Steps 01-07 (except apps/api/src/app.module.ts)
- ONLY create files in
apps/api/src/health/
Task
cd apps/api
# Create health module directory
mkdir -p src/health
# Create src/health/health.controller.ts
cat > src/health/health.controller.ts << 'EOF'
import { Controller, Get } from '@nestjs/common';
import { HealthService } from './health.service';
@Controller('health')
export class HealthController {
constructor(private readonly healthService: HealthService) {}
@Get()
async check() {
return this.healthService.check();
}
}
EOF
# Create src/health/health.service.ts
cat > src/health/health.service.ts << 'EOF'
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma';
@Injectable()
export class HealthService {
constructor(private readonly prisma: PrismaService) {}
async check() {
try {
// Try a simple query to verify database connection
await this.prisma.$queryRaw`SELECT 1`;
return {
status: 'ok',
database: 'connected',
timestamp: new Date().toISOString(),
};
} catch (error) {
return {
status: 'error',
database: 'disconnected',
timestamp: new Date().toISOString(),
};
}
}
}
EOF
# Create src/health/health.module.ts
cat > src/health/health.module.ts << 'EOF'
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
import { HealthService } from './health.service';
@Module({
controllers: [HealthController],
providers: [HealthService],
})
export class HealthModule {}
EOF
# Create src/health/index.ts
cat > src/health/index.ts << 'EOF'
export * from './health.module';
export * from './health.controller';
export * from './health.service';
EOF
# Update app.module.ts to import HealthModule
cat > src/app.module.ts << 'EOF'
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { PrismaModule } from './prisma';
import { HealthModule } from './health';
@Module({
imports: [PrismaModule, HealthModule],
controllers: [AppController],
providers: [],
})
export class AppModule {}
EOF
cd ../..Gate
# Ensure PostgreSQL is running
docker compose up -d
# Start API
cd apps/api && npm run dev &
# Wait for startup, then test health endpoint
sleep 5
curl http://localhost:3001/health
# Should return:
# {"status":"ok","database":"connected","timestamp":"2025-..."}Common Errors
| Error | Cause | Fix |
|---|---|---|
Cannot GET /health | HealthModule not imported | Add HealthModule to AppModule imports |
Nest can't resolve PrismaService | PrismaModule not global | Verify @Global() decorator on PrismaModule |
database: disconnected | PostgreSQL not running | Run docker compose up -d |
Rollback
# Undo Step 08
rm -rf apps/api/src/health
git checkout -- apps/api/src/app.module.tsLock
After this step, these files are locked:
apps/api/src/health/*
Checkpoint
PHASE 00 COMPLETE when:
-
curl localhost:3001/healthreturns{"status":"ok","database":"connected",...} - All three services can run simultaneously
- No errors in any console
- Type "PHASE 00 COMPLETE" to finish
Phase 00 Complete
Verification Checklist
Run these commands to verify Phase 00 is complete:
# 1. Check monorepo structure
ls apps/web apps/api packages/database
# 2. Check PostgreSQL is running
docker compose ps
# 3. Start all services (in separate terminals or use &)
cd apps/web && npm run dev &
cd apps/api && npm run dev &
# 4. Test endpoints
curl http://localhost:3000 # Should return HTML with "HRMS"
curl http://localhost:3001 # Should return "HRMS API Running"
curl http://localhost:3001/health # Should return {"status":"ok","database":"connected",...}Locked Files After Phase 00
docker-compose.yml
apps/web/package.json
apps/web/next.config.ts
apps/api/nest-cli.json
apps/api/tsconfig.json
apps/api/src/prisma/*
apps/api/src/health/*
packages/database/package.json
packages/database/.env.examplePhase Completion Checklist (MANDATORY)
BEFORE MOVING TO NEXT PHASE
Complete ALL items before proceeding. Do NOT skip any step.
1. Gate Verification
- All step gates passed
- Final verification commands successful
- No errors in any console
2. Update PROJECT_STATE.md
# Update these sections:
- Mark Phase 00 as COMPLETED with timestamp
- Add all locked files to "Locked Files" section
- Update "Current Phase" to Phase 01
- Add session log entry3. Update WHAT_EXISTS.md
# Add these items:
## Database Models
- HealthCheck (id, status, checkedAt)
## API Endpoints
- GET /health - Health check with database connection status
## Established Patterns
- PrismaModule pattern: apps/api/src/prisma/
- HealthModule pattern: apps/api/src/health/4. Git Tag & Commit
git add PROJECT_STATE.md WHAT_EXISTS.md
git commit -m "chore: complete Phase 00 - Empty Shell"
git tag phase-00-empty-shell5. Verify State Files
cat PROJECT_STATE.md | grep "Phase 00"
# Should show COMPLETED
cat WHAT_EXISTS.md | grep "HealthCheck"
# Should show the modelNext Phase
After verification, proceed to Phase 01: Multi-Tenant Auth
Last Updated: 2025-11-30