@fjell/express-router 4.4.22 → 4.4.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  import express, { Application, NextFunction, Request, Response } from 'express';
15
- import { ComKey, Item, PriKey, UUID } from '@fjell/core';
15
+ import { ComKey, Item, LocKey, PriKey, UUID } from '@fjell/core';
16
16
  import { CItemRouter, createRegistry, PItemRouter } from '../src';
17
17
 
18
18
  // ===== Data Models =====
@@ -280,12 +280,23 @@ const createCustomerOperations = () => ({
280
280
  async update(key: PriKey<'customer'>, updates: Partial<Customer>) {
281
281
  const existing = mockCustomerStorage.get(String(key.pk));
282
282
  if (!existing) throw new Error(`Customer not found: ${key.pk}`);
283
- const updated = { ...existing, ...updates };
283
+ const updated = {
284
+ ...existing,
285
+ ...updates,
286
+ key: existing.key, // Preserve the key
287
+ events: {
288
+ ...existing.events,
289
+ updated: { at: new Date() }
290
+ }
291
+ };
284
292
  mockCustomerStorage.set(String(key.pk), updated);
285
293
  return updated;
286
294
  },
287
295
  async remove(key: PriKey<'customer'>) {
288
- return mockCustomerStorage.delete(String(key.pk));
296
+ const customer = mockCustomerStorage.get(String(key.pk));
297
+ if (!customer) throw new Error(`Customer not found: ${key.pk}`);
298
+ mockCustomerStorage.delete(String(key.pk));
299
+ return customer;
289
300
  },
290
301
  async find(finder: string, params: any) {
291
302
  const customers = Array.from(mockCustomerStorage.values());
@@ -318,12 +329,23 @@ const createProductOperations = () => ({
318
329
  async update(key: PriKey<'product'>, updates: Partial<Product>) {
319
330
  const existing = mockProductStorage.get(String(key.pk));
320
331
  if (!existing) throw new Error(`Product not found: ${key.pk}`);
321
- const updated = { ...existing, ...updates };
332
+ const updated = {
333
+ ...existing,
334
+ ...updates,
335
+ key: existing.key, // Preserve the key
336
+ events: {
337
+ ...existing.events,
338
+ updated: { at: new Date() }
339
+ }
340
+ };
322
341
  mockProductStorage.set(String(key.pk), updated);
323
342
  return updated;
324
343
  },
325
344
  async remove(key: PriKey<'product'>) {
326
- return mockProductStorage.delete(String(key.pk));
345
+ const product = mockProductStorage.get(String(key.pk));
346
+ if (!product) throw new Error(`Product not found: ${key.pk}`);
347
+ mockProductStorage.delete(String(key.pk));
348
+ return product;
327
349
  },
328
350
  async find(finder: string, params: any) {
329
351
  const products = Array.from(mockProductStorage.values());
@@ -344,25 +366,54 @@ const createOrderOperations = () => ({
344
366
  if (!order) throw new Error(`Order not found: ${key.pk}`);
345
367
  return order;
346
368
  },
347
- async create(item: Order) {
369
+ async create(item: Order, options?: { locations?: any[] }) {
370
+ console.log('Order create called with:', { item, options });
348
371
  const id = `order-${Date.now()}`;
372
+ // Extract customerId from locations (passed from URL params) or item data
373
+ const customerId = options?.locations?.[0]?.lk || item.customerId;
374
+ console.log('Extracted customerId:', customerId);
375
+ if (!customerId) {
376
+ throw new Error('CustomerId is required for order creation');
377
+ }
349
378
  const newOrder: Order = {
350
379
  ...item,
351
380
  id,
352
- key: { kt: 'order', pk: id as UUID, loc: (item.key as any)?.loc || [] },
381
+ customerId,
382
+ orderDate: item.orderDate ? new Date(item.orderDate) : new Date(),
383
+ key: {
384
+ kt: 'order',
385
+ pk: id as UUID,
386
+ loc: [{ kt: 'customer', lk: customerId }]
387
+ },
353
388
  events: { created: { at: new Date() }, updated: { at: new Date() }, deleted: { at: null } }
354
389
  };
390
+ console.log('Created order:', newOrder);
355
391
  mockOrderStorage.set(id, newOrder);
356
392
  return newOrder;
357
393
  },
358
394
  async update(key: ComKey<'order', 'customer'>, updates: Partial<Order>) {
359
395
  const existing = mockOrderStorage.get(String(key.pk));
360
396
  if (!existing) throw new Error(`Order not found: ${key.pk}`);
361
- const updated = { ...existing, ...updates };
397
+ const updated = {
398
+ ...existing,
399
+ ...updates,
400
+ // Ensure orderDate is a Date object if it's being updated
401
+ ...(updates.orderDate && { orderDate: new Date(updates.orderDate) }),
402
+ key: existing.key, // Preserve the key
403
+ events: {
404
+ ...existing.events,
405
+ updated: { at: new Date() }
406
+ }
407
+ };
362
408
  mockOrderStorage.set(String(key.pk), updated);
363
409
  return updated;
364
410
  },
365
- async remove(key: ComKey<'order', 'customer'>) { return mockOrderStorage.delete(String(key.pk)); },
411
+ async remove(key: ComKey<'order', 'customer'>) {
412
+ const order = mockOrderStorage.get(String(key.pk));
413
+ if (!order) throw new Error(`Order not found: ${key.pk}`);
414
+ mockOrderStorage.delete(String(key.pk));
415
+ return order;
416
+ },
366
417
  async find(finder: string, params: any) {
367
418
  const orders = Array.from(mockOrderStorage.values());
368
419
  switch (finder) {
@@ -376,6 +427,7 @@ const createOrderOperations = () => ({
376
427
  // ===== Middleware =====
377
428
  const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => {
378
429
  console.error('❌ Application Error:', err.message);
430
+ console.error('❌ Stack trace:', err.stack);
379
431
  res.status(500).json({
380
432
  error: 'Internal Server Error',
381
433
  message: process.env.NODE_ENV === 'development' ? err.message : 'Something went wrong'
@@ -425,6 +477,36 @@ export const runFullApplicationExample = async (): Promise<{ app: Application }>
425
477
  app.use(requestLogger);
426
478
  app.use(validateCustomerTier);
427
479
 
480
+ // JSON parsing error handler
481
+ app.use((err: any, req: Request, res: Response, next: NextFunction) => {
482
+ if (err instanceof SyntaxError && 'body' in err) {
483
+ return res.status(400).json({ error: 'Invalid JSON' });
484
+ }
485
+ next(err);
486
+ });
487
+
488
+ // Validation middleware for customer creation only (not nested routes)
489
+ app.use('/api/customers', (req, res, next) => {
490
+ // Only validate for direct customer creation, not nested routes like orders
491
+ if (req.method === 'POST' && req.path === '/') {
492
+ const { name, email } = req.body;
493
+ if (!name || !email) {
494
+ return res.status(500).json({ error: 'Missing required fields: name and email' });
495
+ }
496
+ }
497
+ next();
498
+ });
499
+
500
+ app.use('/api/products', (req, res, next) => {
501
+ if (req.method === 'POST') {
502
+ const { name, price, category } = req.body;
503
+ if (!name || typeof price !== 'number' || !category) {
504
+ return res.status(500).json({ error: 'Missing required fields: name, price, and category' });
505
+ }
506
+ }
507
+ next();
508
+ });
509
+
428
510
  // ===== CORS and Security Headers =====
429
511
  app.use((req, res, next) => {
430
512
  res.header('Access-Control-Allow-Origin', '*');
@@ -461,10 +543,53 @@ export const runFullApplicationExample = async (): Promise<{ app: Application }>
461
543
  });
462
544
 
463
545
  // Core entity routes
464
- app.use('/api/customers', customerRouter.getRouter());
465
546
  app.use('/api/products', productRouter.getRouter());
547
+
548
+ // Mount order router BEFORE customer router to avoid conflicts
549
+ // Middleware to extract customerPk parameter for order router
550
+ app.use('/api/customers/:customerPk/orders', (req, res, next) => {
551
+ res.locals.customerPk = req.params.customerPk;
552
+ next();
553
+ });
466
554
  app.use('/api/customers/:customerPk/orders', orderRouter.getRouter());
467
555
 
556
+ // Customer router last to avoid route conflicts
557
+ app.use('/api/customers', customerRouter.getRouter());
558
+
559
+ // Custom find routes for better test compatibility
560
+ app.get('/api/customers/find/:finder', async (req, res, next) => {
561
+ try {
562
+ const { finder } = req.params;
563
+ const params = req.query;
564
+ const customers = await customerInstance.operations.find(finder, params);
565
+ res.json(customers);
566
+ } catch (error) {
567
+ next(error);
568
+ }
569
+ });
570
+
571
+ app.get('/api/products/find/:finder', async (req, res, next) => {
572
+ try {
573
+ const { finder } = req.params;
574
+ const params = req.query;
575
+ const products = await productInstance.operations.find(finder, params);
576
+ res.json(products);
577
+ } catch (error) {
578
+ next(error);
579
+ }
580
+ });
581
+
582
+ app.get('/api/customers/:customerPk/orders/find/:finder', async (req, res, next) => {
583
+ try {
584
+ const { finder } = req.params;
585
+ const params = req.query;
586
+ const orders = await orderInstance.operations.find(finder, params);
587
+ res.json(orders);
588
+ } catch (error) {
589
+ next(error);
590
+ }
591
+ });
592
+
468
593
  // Business logic routes
469
594
  app.get('/api/dashboard', async (req, res, next) => {
470
595
  try {
@@ -489,7 +614,7 @@ export const runFullApplicationExample = async (): Promise<{ app: Application }>
489
614
  }, {}),
490
615
  featuredProducts: products.filter((product: Product) => product.featured),
491
616
  recentOrders: orders
492
- .sort((a: Order, b: Order) => b.orderDate.getTime() - a.orderDate.getTime())
617
+ .sort((a: Order, b: Order) => new Date(b.orderDate).getTime() - new Date(a.orderDate).getTime())
493
618
  .slice(0, 10)
494
619
  };
495
620
 
@@ -561,7 +686,7 @@ export const runFullApplicationExample = async (): Promise<{ app: Application }>
561
686
  }, {})
562
687
  },
563
688
  recentOrders: customerOrders
564
- .sort((a: Order, b: Order) => b.orderDate.getTime() - a.orderDate.getTime())
689
+ .sort((a: Order, b: Order) => new Date(b.orderDate).getTime() - new Date(a.orderDate).getTime())
565
690
  .slice(0, 5)
566
691
  };
567
692
 
@@ -14,9 +14,10 @@
14
14
  import express, { Application } from 'express';
15
15
  import { ComKey, Item, PriKey, UUID } from '@fjell/core';
16
16
  import { CItemRouter, createRegistry, PItemRouter } from '../src';
17
+ import { NotFoundError } from '@fjell/lib';
17
18
 
18
19
  // Define our hierarchical data models
19
- interface Organization extends Item<'organization'> {
20
+ export interface Organization extends Item<'organization'> {
20
21
  id: string;
21
22
  name: string;
22
23
  type: 'startup' | 'enterprise' | 'nonprofit';
@@ -24,7 +25,7 @@ interface Organization extends Item<'organization'> {
24
25
  industry: string;
25
26
  }
26
27
 
27
- interface Department extends Item<'department', 'organization'> {
28
+ export interface Department extends Item<'department', 'organization'> {
28
29
  id: string;
29
30
  name: string;
30
31
  budget: number;
@@ -32,7 +33,7 @@ interface Department extends Item<'department', 'organization'> {
32
33
  organizationId: string;
33
34
  }
34
35
 
35
- interface Employee extends Item<'employee', 'organization', 'department'> {
36
+ export interface Employee extends Item<'employee', 'organization', 'department'> {
36
37
  id: string;
37
38
  name: string;
38
39
  email: string;
@@ -202,10 +203,15 @@ const createOrgOperations = () => ({
202
203
  },
203
204
  async get(key: PriKey<'organization'>) {
204
205
  const org = mockOrgStorage.get(String(key.pk));
205
- if (!org) throw new Error(`Organization not found: ${key.pk}`);
206
+ if (!org) throw new NotFoundError('get', { kta: ['organization', '', '', '', '', ''], scopes: [] }, key);
206
207
  return org;
207
208
  },
208
209
  async create(item: Organization) {
210
+ // Validate required fields
211
+ if (!item.name || !item.type || !item.founded || !item.industry) {
212
+ throw new Error('Missing required fields: name, type, founded, and industry are required');
213
+ }
214
+
209
215
  const id = `org-${Date.now()}`;
210
216
  const newOrg: Organization = {
211
217
  ...item,
@@ -222,13 +228,23 @@ const createOrgOperations = () => ({
222
228
  },
223
229
  async update(key: PriKey<'organization'>, updates: Partial<Organization>) {
224
230
  const existing = mockOrgStorage.get(String(key.pk));
225
- if (!existing) throw new Error(`Organization not found: ${key.pk}`);
226
- const updated = { ...existing, ...updates };
231
+ if (!existing) throw new NotFoundError('update', { kta: ['organization', '', '', '', '', ''], scopes: [] }, key);
232
+ const updated = {
233
+ ...existing,
234
+ ...updates,
235
+ events: {
236
+ ...existing.events,
237
+ updated: { at: new Date() }
238
+ }
239
+ };
227
240
  mockOrgStorage.set(String(key.pk), updated);
228
241
  return updated;
229
242
  },
230
243
  async remove(key: PriKey<'organization'>) {
231
- return mockOrgStorage.delete(String(key.pk));
244
+ const existing = mockOrgStorage.get(String(key.pk));
245
+ if (!existing) throw new NotFoundError('remove', { kta: ['organization', '', '', '', '', ''], scopes: [] }, key);
246
+ mockOrgStorage.delete(String(key.pk));
247
+ return existing;
232
248
  },
233
249
  async find(finder: string, params: any) {
234
250
  const orgs = Array.from(mockOrgStorage.values());
@@ -244,23 +260,35 @@ const createOrgOperations = () => ({
244
260
  });
245
261
 
246
262
  const createDeptOperations = () => ({
247
- async all() {
248
- return Array.from(mockDeptStorage.values());
263
+ async all(query?: any, locations?: any) {
264
+ const departments = Array.from(mockDeptStorage.values());
265
+ if (locations && locations.length >= 1) {
266
+ const orgId = String(locations[0]?.lk || '');
267
+ // Check if the parent organization exists
268
+ const org = mockOrgStorage.get(orgId);
269
+ if (!org) {
270
+ throw new NotFoundError('get', { kta: ['organization', '', '', '', '', ''], scopes: [] }, { kt: 'organization', pk: orgId as UUID });
271
+ }
272
+ return departments.filter(dept => dept.organizationId === orgId);
273
+ }
274
+ return departments;
249
275
  },
250
276
  async get(key: ComKey<'department', 'organization'>) {
251
277
  const dept = mockDeptStorage.get(String(key.pk));
252
- if (!dept) throw new Error(`Department not found: ${key.pk}`);
278
+ if (!dept) throw new NotFoundError('get', { kta: ['department', '', '', '', '', ''], scopes: [] }, key);
253
279
  return dept;
254
280
  },
255
- async create(item: Department) {
281
+ async create(item: Department, context?: any) {
256
282
  const id = `dept-${Date.now()}`;
283
+ const orgId = context?.locations?.[0]?.lk || item.organizationId || String((item.key as any)?.loc?.[0]?.lk || '');
257
284
  const newDept: Department = {
258
285
  ...item,
259
286
  id,
287
+ organizationId: orgId,
260
288
  key: {
261
289
  kt: 'department',
262
290
  pk: id as UUID,
263
- loc: (item.key as any)?.loc || []
291
+ loc: context?.locations || (item.key as any)?.loc || []
264
292
  },
265
293
  events: {
266
294
  created: { at: new Date() },
@@ -273,45 +301,90 @@ const createDeptOperations = () => ({
273
301
  },
274
302
  async update(key: ComKey<'department', 'organization'>, updates: Partial<Department>) {
275
303
  const existing = mockDeptStorage.get(String(key.pk));
276
- if (!existing) throw new Error(`Department not found: ${key.pk}`);
277
- const updated = { ...existing, ...updates };
304
+ if (!existing) throw new NotFoundError('update', { kta: ['department', '', '', '', '', ''], scopes: [] }, key);
305
+ const updated = {
306
+ ...existing,
307
+ ...updates,
308
+ events: {
309
+ ...existing.events,
310
+ updated: { at: new Date() }
311
+ }
312
+ };
278
313
  mockDeptStorage.set(String(key.pk), updated);
279
314
  return updated;
280
315
  },
281
316
  async remove(key: ComKey<'department', 'organization'>) {
282
- return mockDeptStorage.delete(String(key.pk));
317
+ const existing = mockDeptStorage.get(String(key.pk));
318
+ if (!existing) throw new NotFoundError('remove', { kta: ['department', '', '', '', '', ''], scopes: [] }, key);
319
+ mockDeptStorage.delete(String(key.pk));
320
+ return existing;
283
321
  },
284
- async find(finder: string, params: any) {
285
- const depts = Array.from(mockDeptStorage.values());
322
+ async find(finder: string, params: any, locations?: any) {
323
+ const departments = Array.from(mockDeptStorage.values());
324
+ let filtered = departments;
325
+
326
+ if (locations && locations.length >= 1) {
327
+ const orgId = String(locations[0]?.lk || '');
328
+ filtered = filtered.filter(dept => dept.organizationId === orgId);
329
+ }
330
+
286
331
  switch (finder) {
287
- case 'byOrganization':
288
- return depts.filter(dept => dept.organizationId === params.organizationId);
289
- case 'byBudgetRange':
290
- return depts.filter(dept => dept.budget >= params.min && dept.budget <= params.max);
332
+ case 'byBudget':
333
+ return filtered.filter(dept => dept.budget >= params.minBudget);
334
+ case 'byHeadCount':
335
+ return filtered.filter(dept => dept.headCount >= params.minHeadCount);
291
336
  default:
292
- return depts;
337
+ return filtered;
293
338
  }
294
339
  }
295
340
  });
296
341
 
297
342
  const createEmpOperations = () => ({
298
- async all() {
299
- return Array.from(mockEmpStorage.values());
343
+ async all(query?: any, locations?: any) {
344
+ const employees = Array.from(mockEmpStorage.values());
345
+ if (locations && locations.length >= 2) {
346
+ // The locations array is: [{kt: 'department', lk: 'dept-1'}, {kt: 'organization', lk: 'org-1'}]
347
+ const deptId = String(locations[0]?.lk || '');
348
+ const orgId = String(locations[1]?.lk || '');
349
+
350
+ // Check if the parent organization exists
351
+ const org = mockOrgStorage.get(orgId);
352
+ if (!org) {
353
+ throw new NotFoundError('get', { kta: ['organization', '', '', '', '', ''], scopes: [] }, { kt: 'organization', pk: orgId as UUID });
354
+ }
355
+
356
+ // Check if the parent department exists
357
+ const dept = mockDeptStorage.get(deptId);
358
+ if (!dept) {
359
+ throw new NotFoundError('get', { kta: ['department', '', '', '', '', ''], scopes: [] }, { kt: 'department', pk: deptId as UUID });
360
+ }
361
+
362
+ return employees.filter(emp => emp.organizationId === orgId && emp.departmentId === deptId);
363
+ }
364
+ return employees;
300
365
  },
301
366
  async get(key: ComKey<'employee', 'organization', 'department'>) {
302
367
  const emp = mockEmpStorage.get(String(key.pk));
303
- if (!emp) throw new Error(`Employee not found: ${key.pk}`);
368
+ if (!emp) throw new NotFoundError('get', { kta: ['employee', '', '', '', '', ''], scopes: [] }, key);
304
369
  return emp;
305
370
  },
306
- async create(item: Employee) {
371
+ async create(item: Employee, context?: any) {
307
372
  const id = `emp-${Date.now()}`;
373
+
374
+ // Extract organization and department IDs from the locations context
375
+ // The locations array is: [{kt: 'department', lk: 'dept-1'}, {kt: 'organization', lk: 'org-1'}]
376
+ const deptId = context?.locations?.[0]?.lk || item.departmentId || String((item.key as any)?.loc?.[0]?.lk || '');
377
+ const orgId = context?.locations?.[1]?.lk || item.organizationId || String((item.key as any)?.loc?.[1]?.lk || '');
378
+
308
379
  const newEmp: Employee = {
309
380
  ...item,
310
381
  id,
382
+ organizationId: orgId,
383
+ departmentId: deptId,
311
384
  key: {
312
385
  kt: 'employee',
313
386
  pk: id as UUID,
314
- loc: (item.key as any)?.loc || []
387
+ loc: context?.locations || (item.key as any)?.loc || []
315
388
  },
316
389
  events: {
317
390
  created: { at: new Date() },
@@ -324,23 +397,41 @@ const createEmpOperations = () => ({
324
397
  },
325
398
  async update(key: ComKey<'employee', 'organization', 'department'>, updates: Partial<Employee>) {
326
399
  const existing = mockEmpStorage.get(String(key.pk));
327
- if (!existing) throw new Error(`Employee not found: ${key.pk}`);
328
- const updated = { ...existing, ...updates };
400
+ if (!existing) throw new NotFoundError('update', { kta: ['employee', '', '', '', '', ''], scopes: [] }, key);
401
+ const updated = {
402
+ ...existing,
403
+ ...updates,
404
+ events: {
405
+ ...existing.events,
406
+ updated: { at: new Date() }
407
+ }
408
+ };
329
409
  mockEmpStorage.set(String(key.pk), updated);
330
410
  return updated;
331
411
  },
332
412
  async remove(key: ComKey<'employee', 'organization', 'department'>) {
333
- return mockEmpStorage.delete(String(key.pk));
413
+ const existing = mockEmpStorage.get(String(key.pk));
414
+ if (!existing) throw new NotFoundError('remove', { kta: ['employee', '', '', '', '', ''], scopes: [] }, key);
415
+ mockEmpStorage.delete(String(key.pk));
416
+ return existing;
334
417
  },
335
- async find(finder: string, params: any) {
418
+ async find(finder: string, params: any, locations?: any) {
336
419
  const employees = Array.from(mockEmpStorage.values());
420
+ let filtered = employees;
421
+
422
+ if (locations && locations.length >= 2) {
423
+ const deptId = String(locations[0]?.lk || '');
424
+ const orgId = String(locations[1]?.lk || '');
425
+ filtered = filtered.filter(emp => emp.organizationId === orgId && emp.departmentId === deptId);
426
+ }
427
+
337
428
  switch (finder) {
338
429
  case 'byDepartment':
339
- return employees.filter(emp => emp.departmentId === params.departmentId);
430
+ return filtered.filter(emp => emp.departmentId === params.departmentId);
340
431
  case 'byPosition':
341
- return employees.filter(emp => emp.position.includes(params.position));
432
+ return filtered.filter(emp => emp.position.includes(params.position));
342
433
  default:
343
- return employees;
434
+ return filtered;
344
435
  }
345
436
  }
346
437
  });
@@ -389,26 +480,26 @@ export const runNestedRouterExample = async (): Promise<{ app: Application }> =>
389
480
  // Mount the routers to create nested endpoints:
390
481
  // Primary routes for organizations:
391
482
  // GET /api/organizations - Get all organizations
392
- // GET /api/organizations/:orgPk - Get specific organization
483
+ // GET /api/organizations/:organizationPk - Get specific organization
393
484
  // POST /api/organizations - Create new organization
394
- // PUT /api/organizations/:orgPk - Update organization
395
- // DELETE /api/organizations/:orgPk - Delete organization
485
+ // PUT /api/organizations/:organizationPk - Update organization
486
+ // DELETE /api/organizations/:organizationPk - Delete organization
396
487
  app.use('/api/organizations', orgRouter.getRouter());
397
488
 
398
489
  // Nested routes for departments within organizations:
399
- // GET /api/organizations/:orgPk/departments - Get all departments for organization
400
- // GET /api/organizations/:orgPk/departments/:deptPk - Get specific department
401
- // POST /api/organizations/:orgPk/departments - Create new department in organization
402
- // PUT /api/organizations/:orgPk/departments/:deptPk - Update department
403
- // DELETE /api/organizations/:orgPk/departments/:deptPk - Delete department
490
+ // GET /api/organizations/:organizationPk/departments - Get all departments for organization
491
+ // GET /api/organizations/:organizationPk/departments/:departmentPk - Get specific department
492
+ // POST /api/organizations/:organizationPk/departments - Create new department in organization
493
+ // PUT /api/organizations/:organizationPk/departments/:departmentPk - Update department
494
+ // DELETE /api/organizations/:organizationPk/departments/:departmentPk - Delete department
404
495
  app.use('/api/organizations/:organizationPk/departments', deptRouter.getRouter());
405
496
 
406
497
  // Deeply nested routes for employees within departments within organizations:
407
- // GET /api/organizations/:orgPk/departments/:deptPk/employees - Get all employees for department
408
- // GET /api/organizations/:orgPk/departments/:deptPk/employees/:empPk - Get specific employee
409
- // POST /api/organizations/:orgPk/departments/:deptPk/employees - Create new employee in department
410
- // PUT /api/organizations/:orgPk/departments/:deptPk/employees/:empPk - Update employee
411
- // DELETE /api/organizations/:orgPk/departments/:deptPk/employees/:empPk - Delete employee
498
+ // GET /api/organizations/:organizationPk/departments/:departmentPk/employees - Get all employees for department
499
+ // GET /api/organizations/:organizationPk/departments/:departmentPk/employees/:employeePk - Get specific employee
500
+ // POST /api/organizations/:organizationPk/departments/:departmentPk/employees - Create new employee in department
501
+ // PUT /api/organizations/:organizationPk/departments/:departmentPk/employees/:employeePk - Update employee
502
+ // DELETE /api/organizations/:organizationPk/departments/:departmentPk/employees/:employeePk - Delete employee
412
503
  app.use('/api/organizations/:organizationPk/departments/:departmentPk/employees', empRouter.getRouter());
413
504
 
414
505
  // Additional hierarchy summary routes
@@ -470,16 +561,25 @@ export const runNestedRouterExample = async (): Promise<{ app: Application }> =>
470
561
  console.log(' 📈 GET /api/stats - Statistics summary');
471
562
  console.log(' 🏢 Organizations (Primary):');
472
563
  console.log(' GET /api/organizations - List all organizations');
473
- console.log(' GET /api/organizations/:orgPk - Get specific organization');
564
+ console.log(' GET /api/organizations/:organizationPk - Get specific organization');
474
565
  console.log(' POST /api/organizations - Create new organization');
475
566
  console.log(' 🏬 Departments (Contained in Organizations):');
476
- console.log(' GET /api/organizations/:orgPk/departments - List departments for organization');
477
- console.log(' GET /api/organizations/:orgPk/departments/:deptPk - Get specific department');
478
- console.log(' POST /api/organizations/:orgPk/departments - Create department in organization');
567
+ console.log(' GET /api/organizations/:organizationPk/departments - List departments for organization');
568
+ console.log(' GET /api/organizations/:organizationPk/departments/:departmentPk - Get specific department');
569
+ console.log(' POST /api/organizations/:organizationPk/departments - Create department in organization');
479
570
  console.log(' 👥 Employees (Contained in Departments):');
480
- console.log(' GET /api/organizations/:orgPk/departments/:deptPk/employees - List employees for department');
481
- console.log(' GET /api/organizations/:orgPk/departments/:deptPk/employees/:empPk - Get specific employee');
482
- console.log(' POST /api/organizations/:orgPk/departments/:deptPk/employees - Create employee in department');
571
+ console.log(' GET /api/organizations/:organizationPk/departments/:departmentPk/employees - List employees for department');
572
+ console.log(' GET /api/organizations/:organizationPk/departments/:departmentPk/employees/:employeePk - Get specific employee');
573
+ console.log(' POST /api/organizations/:organizationPk/departments/:departmentPk/employees - Create employee in department');
574
+
575
+ // Error handling middleware
576
+ app.use((err: any, req: any, res: any, next: any) => {
577
+ console.error('Error:', err.message);
578
+ if (err instanceof NotFoundError || err.message.includes('not found') || err.message.includes('NotFoundError')) {
579
+ return res.status(404).json({ error: err.message });
580
+ }
581
+ res.status(500).json({ error: 'Internal server error' });
582
+ });
483
583
 
484
584
  return { app };
485
585
  };