AI Development GuideBootstrap
Bootstrap Artifacts
Copy-paste ready files to bootstrap the HRMS project
Bootstrap Artifacts
These are the essential files needed to bootstrap the HRMS project. Copy these exactly to get started.
Environment Variables
Create .env.example in the project root:
# Database
DATABASE_URL="postgresql://postgres:password@localhost:5432/hrms_dev?schema=public"
# Auth.js
AUTH_SECRET="your-auth-secret-here-generate-with-openssl-rand-base64-32"
AUTH_URL="http://localhost:3000"
# AI Service
AI_SERVICE_URL="http://localhost:3001"
OPENAI_API_KEY="sk-your-openai-api-key"
# MongoDB (for AI service)
MONGODB_URI="mongodb://localhost:27017/hrms_ai"
# Google Cloud Storage
GCS_BUCKET_NAME="hrms-documents"
GCS_PROJECT_ID="your-gcp-project-id"
GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
# Redis (future enhancement - not required for MVP)
# REDIS_URL="redis://localhost:6379"
# Application
NODE_ENV="development"
PORT=3000
API_PREFIX="/api/v1"
# JWT
JWT_SECRET="your-jwt-secret-here"
JWT_EXPIRES_IN="1h"
JWT_REFRESH_EXPIRES_IN="7d"Docker Compose
Create docker-compose.yml for local development:
version: '3.8'
services:
postgres:
image: postgres:17-alpine
container_name: hrms_postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: hrms_dev
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
mongodb:
image: mongo:7
container_name: hrms_mongodb
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 5s
timeout: 5s
retries: 5
# Redis - optional for caching (Phase 10+)
# redis:
# image: redis:7-alpine
# container_name: hrms_redis
# ports:
# - "6379:6379"
# volumes:
# - redis_data:/data
# healthcheck:
# test: ["CMD", "redis-cli", "ping"]
# interval: 5s
# timeout: 5s
# retries: 5
volumes:
postgres_data:
mongodb_data:
# redis_data:Environment Validation
Create src/config/env.schema.ts:
import { z } from 'zod';
export const envSchema = z.object({
// Database
DATABASE_URL: z.string().url(),
// Auth
AUTH_SECRET: z.string().min(32),
AUTH_URL: z.string().url(),
// AI Service
AI_SERVICE_URL: z.string().url(),
OPENAI_API_KEY: z.string().startsWith('sk-'),
// MongoDB
MONGODB_URI: z.string().startsWith('mongodb'),
// GCS (optional in dev)
GCS_BUCKET_NAME: z.string().optional(),
GCS_PROJECT_ID: z.string().optional(),
GOOGLE_APPLICATION_CREDENTIALS: z.string().optional(),
// Redis (optional - future enhancement)
REDIS_URL: z.string().url().optional(),
// Application
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
PORT: z.coerce.number().default(3000),
API_PREFIX: z.string().default('/api/v1'),
// JWT
JWT_SECRET: z.string().min(32),
JWT_EXPIRES_IN: z.string().default('1h'),
JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),
});
export type EnvConfig = z.infer<typeof envSchema>;
export function validateEnv(): EnvConfig {
const result = envSchema.safeParse(process.env);
if (!result.success) {
console.error('Invalid environment variables:');
console.error(result.error.format());
process.exit(1);
}
return result.data;
}Project Folder Structure
hrms/
├── apps/
│ ├── api/ # NestJS Backend
│ │ ├── src/
│ │ │ ├── config/ # Configuration (env validation)
│ │ │ ├── common/ # Shared utilities, guards, decorators
│ │ │ │ ├── guards/ # Auth, Permission, Tenant guards
│ │ │ │ ├── decorators/ # Custom decorators (@TenantId, @CurrentUser)
│ │ │ │ ├── filters/ # Exception filters
│ │ │ │ ├── interceptors/# Response interceptors
│ │ │ │ └── dto/ # Base DTOs
│ │ │ ├── modules/ # Feature modules
│ │ │ │ ├── auth/ # Authentication module
│ │ │ │ ├── tenant/ # Tenant module
│ │ │ │ ├── employee/ # Employee module
│ │ │ │ ├── department/ # Department module
│ │ │ │ ├── team/ # Team module
│ │ │ │ ├── time-off/ # Time-off module
│ │ │ │ ├── document/ # Document module
│ │ │ │ ├── dashboard/ # Dashboard module
│ │ │ │ ├── goal/ # Goal module
│ │ │ │ └── feed/ # Team feed module
│ │ │ ├── prisma/ # Prisma service and module
│ │ │ ├── app.module.ts
│ │ │ └── main.ts
│ │ ├── prisma/
│ │ │ ├── schema.prisma
│ │ │ ├── migrations/
│ │ │ └── seed.ts
│ │ ├── test/
│ │ └── package.json
│ │
│ └── web/ # Next.js Frontend
│ │ ├── src/
│ │ │ ├── app/ # App Router pages
│ │ │ │ ├── (auth)/ # Auth pages (login, register)
│ │ │ │ ├── (dashboard)/ # Dashboard pages
│ │ │ │ └── api/ # API routes
│ │ │ ├── components/ # React components
│ │ │ │ ├── ui/ # Base UI components
│ │ │ │ ├── forms/ # Form components
│ │ │ │ └── features/ # Feature-specific components
│ │ │ ├── hooks/ # Custom hooks
│ │ │ ├── lib/ # Utilities
│ │ │ ├── providers/ # Context providers
│ │ │ └── types/ # TypeScript types
│ │ └── package.json
│ │
│
├── packages/
│ ├── database/ # Shared Prisma client
│ │ ├── prisma/
│ │ │ └── schema.prisma
│ │ └── package.json
│ │
│ └── hrms-ai/ # Express AI Service
│ ├── src/
│ │ ├── config/
│ │ ├── routes/
│ │ ├── services/
│ │ │ ├── embedding.service.ts
│ │ │ ├── rag.service.ts
│ │ │ └── chat.service.ts
│ │ ├── db/ # MongoDB connection
│ │ └── index.ts
│ └── package.json
│
├── docker-compose.yml
├── .env.example
├── .cursorrules # Cursor AI rules
├── CLAUDE.md # Claude Code rules
└── package.json # Workspace rootPackage.json (Workspace Root)
{
"name": "hrms",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"dev": "npm run dev --workspaces --if-present",
"dev:api": "npm run dev -w api",
"dev:web": "npm run dev -w web",
"dev:ai": "npm run dev -w hrms-ai",
"build": "npm run build --workspaces --if-present",
"test": "npm test --workspaces --if-present",
"lint": "npm run lint --workspaces --if-present",
"db:generate": "npm run prisma generate -w database",
"db:migrate": "npm run prisma migrate dev -w database",
"db:seed": "npm run prisma db seed -w database",
"db:studio": "npm run prisma studio -w database"
},
"devDependencies": {
"typescript": "^5.7.3"
},
"engines": {
"node": ">=20.0.0"
}
}Prisma Seed Script
Create packages/database/prisma/seed.ts:
import { PrismaClient, SystemRole } from '@prisma/client';
import * as bcrypt from 'bcrypt';
const prisma = new PrismaClient();
async function main() {
console.log('Seeding database...');
// Create default tenant
const tenant = await prisma.tenant.upsert({
where: { domain: 'demo.hrms.local' },
update: {},
create: {
name: 'Demo Company',
domain: 'demo.hrms.local',
status: 'ACTIVE',
settings: {
timezone: 'America/New_York',
dateFormat: 'MM/DD/YYYY',
currency: 'USD',
},
},
});
console.log(`Created tenant: ${tenant.name}`);
// Create system admin user
const adminPasswordHash = await bcrypt.hash('Admin123!', 10);
const admin = await prisma.user.upsert({
where: {
tenantId_email: {
tenantId: tenant.id,
email: 'admin@demo.hrms.local',
},
},
update: {},
create: {
tenantId: tenant.id,
email: 'admin@demo.hrms.local',
name: 'System Admin',
systemRole: 'SYSTEM_ADMIN',
status: 'ACTIVE',
},
});
console.log(`Created admin user: ${admin.email}`);
// Create demo employee
const employee = await prisma.employee.upsert({
where: {
tenantId_email: {
tenantId: tenant.id,
email: 'john.doe@demo.hrms.local',
},
},
update: {},
create: {
tenantId: tenant.id,
employeeNumber: 'EMP001',
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@demo.hrms.local',
jobTitle: 'Software Engineer',
employmentType: 'FULL_TIME',
workMode: 'HYBRID',
status: 'ACTIVE',
hireDate: new Date('2024-01-15'),
},
});
console.log(`Created employee: ${employee.firstName} ${employee.lastName}`);
// Create demo department
const engineering = await prisma.department.upsert({
where: {
tenantId_code: {
tenantId: tenant.id,
code: 'ENG',
},
},
update: {},
create: {
tenantId: tenant.id,
name: 'Engineering',
code: 'ENG',
description: 'Engineering department',
status: 'ACTIVE',
},
});
console.log(`Created department: ${engineering.name}`);
// Create demo team
const backendTeam = await prisma.team.upsert({
where: { id: 'backend-team-seed' },
update: {},
create: {
id: 'backend-team-seed',
tenantId: tenant.id,
name: 'Backend Team',
description: 'Backend development team',
type: 'PERMANENT',
status: 'ACTIVE',
},
});
console.log(`Created team: ${backendTeam.name}`);
// Create time-off policy
const vacationPolicy = await prisma.timeOffPolicy.upsert({
where: {
tenantId_code: {
tenantId: tenant.id,
code: 'VACATION',
},
},
update: {},
create: {
tenantId: tenant.id,
name: 'Vacation',
code: 'VACATION',
description: 'Annual vacation leave',
leaveType: 'VACATION',
accrualType: 'ANNUAL',
annualAllowance: 20,
carryOverLimit: 5,
requiresApproval: true,
status: 'ACTIVE',
},
});
console.log(`Created time-off policy: ${vacationPolicy.name}`);
console.log('Seeding completed!');
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});Health Check Endpoint
Add to apps/api/src/app.controller.ts:
import { Controller, Get } from '@nestjs/common';
@Controller()
export class AppController {
@Get('health')
healthCheck() {
return {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
};
}
@Get('api/health')
apiHealthCheck() {
return {
status: 'ok',
timestamp: new Date().toISOString(),
version: '1.0.0',
};
}
}Quick Start Commands
# 1. Clone and install
git clone <repo-url>
cd hrms
npm install
# 2. Start infrastructure
docker compose up -d
# 3. Setup database
npm run db:generate
npm run db:migrate
npm run db:seed
# 4. Start development servers
npm run dev
# Backend: http://localhost:3000
# Frontend: http://localhost:3001
# AI Service: http://localhost:3002Next Steps
After bootstrapping, proceed to: