A full-stack Single Page Application built with Angular 14+ and a Node.js/Express TypeScript backend.
nexusapp/
├── client/ # Angular SPA
│ ├── src/
│ │ ├── app/
│ │ │ ├── core/ # CoreModule — singleton services
│ │ │ │ ├── services/
│ │ │ │ │ ├── auth.service.ts # JWT auth, session management
│ │ │ │ │ ├── user.service.ts # User CRUD API calls
│ │ │ │ │ └── records.service.ts # Records API with delay param
│ │ │ │ ├── guards/
│ │ │ │ │ ├── auth.guard.ts # Redirect unauthenticated users
│ │ │ │ │ └── role.guard.ts # Restrict Admin routes
│ │ │ │ └── interceptors/
│ │ │ │ └── jwt.interceptor.ts # Attach Bearer token to all requests
│ │ │ ├── features/ # Lazy-loaded feature modules
│ │ │ │ ├── auth/ # AuthModule — Login page
│ │ │ │ ├── dashboard/ # DashboardModule — Profile + Records table
│ │ │ │ └── admin/ # AdminModule — User CRUD (Admin only)
│ │ │ ├── shared/
│ │ │ │ ├── components/ # Reusable components (skeleton, badges)
│ │ │ │ └── models/ # TypeScript interfaces: User, AppRecord
│ │ │ ├── app-routing.module.ts
│ │ │ └── app.module.ts
│ │ └── environments/
│ │ ├── environment.ts # Dev: apiBaseUrl, apiDelay
│ │ └── environment.prod.ts # Prod config
│ ├── index.html # Standalone SPA (open directly, no build needed)
│ ├── proxy.conf.json # Dev proxy → localhost:3000
│ └── package.json
│
└── server/ # Express TypeScript API
├── src/
│ ├── data/
│ │ └── db.ts # In-memory DB (NoSQL document style) + seeder
│ ├── middleware/
│ │ └── auth.ts # JWT verify, AdminGuard, Delay middleware
│ ├── routes/
│ │ ├── auth.ts # POST /api/auth/login
│ │ ├── records.ts # GET /api/records?delay=2000
│ │ └── users.ts # CRUD /api/users
│ └── index.ts # Express app entry point
├── package.json
└── tsconfig.json
Open a terminal in VS Code and run:
cd server
npm install
npm run devWait until you see:
✅ Seeded 4 users and 24 records
🚀 NexusApp API running on http://localhost:3000
⚠️ Keep this terminal running at all times. Closing it will break login.
In the VS Code File Explorer, right-click client/index.html and select "Open with Live Server".
Your browser will open automatically. Make sure the URL shows:
http://localhost:5500/client/index.html
⚠️ Do NOT use the IP address (e.g. 192.168.x.x). Always uselocalhost.
Open this in your browser before logging in:
http://localhost:3000/api/health
You should see:
{ "status": "ok", "timestamp": "...", "service": "NexusApp API" }| User ID | Password | Role | Department |
|---|---|---|---|
| admin01 | Admin@123 | Admin | Engineering |
| admin02 | Admin@456 | Admin | Operations |
| user01 | User@123 | General | Finance |
| user02 | User@456 | General | Marketing |
💡 Use the "Use" buttons on the login page to auto-fill credentials instantly.
The API base URL in client/index.html was updated to use localhost explicitly
to avoid CORS issues when Live Server opens the client on a network IP:
// Fixed line in client/index.html
const BASE = 'http://localhost:3000/api';The server CORS policy in server/src/index.ts was also updated to allow all origins:
// Fixed line in server/src/index.ts
app.use(cors());| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/auth/login | None | Authenticate user, returns JWT |
| GET | /api/records?delay=N | JWT | Get records (role-filtered, delayed) |
| GET | /api/users | JWT+Admin | List all users |
| POST | /api/users | JWT+Admin | Create new user |
| PUT | /api/users/:id | JWT+Admin | Update user |
| DELETE | /api/users/:id | JWT+Admin | Delete user |
| GET | /api/health | None | Server health check |
- Lazy-loaded modules:
AuthModule,DashboardModule,AdminModule - CoreModule singletons:
AuthService,UserService,RecordsService - Guards:
AuthGuard(blocks unauthenticated),AdminGuard(blocks non-admin) - JWT Interceptor: Auto-attaches
Authorization: Bearer <token>to every request - Reactive Forms: Validation on login fields
- Angular Material:
mat-tablewith sorting/pagination, dialogs, spinners - Environment config:
environment.tsfor API base URL and default delay - Async demo:
?delay=Nparameter + skeleton loader + progress bar
- Express + TypeScript: Strict typing, compile-time safety
- In-memory DB: NoSQL document-style
{ users[], records[] }— no setup needed - Middleware chain:
authMiddleware→delayMiddleware→ route handler - bcryptjs: Passwords hashed at seed time, compared at login
- JWT: 8-hour tokens, verified on every protected route
- CORS: Open to all origins (
cors()) for local development
The dashboard fetches records with a configurable API delay:
GET /api/records?delay=2000
While loading, the UI shows:
- Async banner with spinner and delay label
- Animated progress bar tracking elapsed time
- Skeleton rows in the table (Angular Material style)
The delay can be changed via dropdown (500ms – 3000ms) without refreshing.
| Feature | General User | Admin |
|---|---|---|
| Login | ✅ | ✅ |
| Dashboard & Profile | ✅ | ✅ |
| View own records | ✅ | ✅ |
| View all records | ❌ | ✅ |
| Admin nav item | ❌ | ✅ |
| /admin/users route | ❌ (redirected) | ✅ |
| CRUD users | ❌ | ✅ |
| Problem | Fix |
|---|---|
| "Failed to fetch" on login | Make sure server is running: cd server && npm run dev |
| CORS error in browser console | Use http://localhost:5500 not the IP address |
ts-node-dev not recognized |
Run npm install inside the server/ folder first |
| Port 3000 already in use | Run npx kill-port 3000 then restart server |
| Live Server not available | Install Live Server extension by Ritwick Dey in VS Code |
- Font: DM Sans (UI) + Space Mono (data/code)
- Theme: Deep navy dark mode with electric blue accent
- Color tokens: CSS custom properties for full theme control
- Animations: Page fade-in, skeleton shimmer, toast slide-in, login card reveal