Skip to content

AI Prompt: Forma3D.Connect — Phase 5f: Shared API Types

Purpose: This prompt instructs an AI to eliminate duplicate DTO definitions by creating shared types between frontend and backend
Estimated Effort: 2-3 days (~12-18 hours)
Prerequisites: Phase 5e completed (Typed JSON Schemas)
Output: Unified API types in libs/domain-contracts, type-safe frontend API client
Status: 🟡 PENDING


🎯 Mission

You are continuing development of Forma3D.Connect, building on the Phase 5e foundation. Your task is to implement Phase 5f: Shared API Types — specifically addressing TD-004 (Duplicated DTO Definitions Between Frontend and Backend) from the technical debt register.

Why This Matters:

The frontend manually defines response types that duplicate backend DTOs, causing:

  1. Type Drift: Frontend/backend types become inconsistent over time
  2. Double Maintenance: Every API change requires updates in two places
  3. No Compile-Time Safety: Mismatches only caught at runtime
  4. Nullable Confusion: Frontend may assume non-null when backend returns null

Phase 5f delivers:

  • Unified API response types in libs/domain-contracts
  • Frontend API client using shared types
  • Single source of truth for all API contracts
  • Compile-time type checking across the stack

📋 Context: Technical Debt Item

TD-004: Duplicated DTO Definitions Between Frontend and Backend

Attribute Value
Type Architecture Debt
Priority High
Location apps/web/src/lib/api-client.ts and apps/api/src/**/dto/*.ts
Interest Rate Medium-High (sync issues cause bugs)
Principal (Effort) 2-3 days

Current State

Duplicated Types:

Type Frontend Location Backend Location
OrderResponse api-client.ts order.dto.ts
PrintJobResponse api-client.ts print-job.dto.ts
DashboardStats api-client.ts dashboard.dto.ts
ProductMappingResponse api-client.ts product-mapping.dto.ts
ShipmentResponse api-client.ts shipment.dto.ts
EventLogResponse api-client.ts event-log.dto.ts

🛠️ Implementation Phases

Phase 1: Create Shared API Response Types (4 hours)

Priority: Critical | Impact: High | Dependencies: None

1. Create API Response Types Directory

Create libs/domain-contracts/src/api/ directory structure:

libs/domain-contracts/src/api/
  index.ts
  common.types.ts
  order.api.ts
  print-job.api.ts
  dashboard.api.ts
  product-mapping.api.ts
  shipment.api.ts
  event-log.api.ts
  health.api.ts

2. Create Common Types

Create libs/domain-contracts/src/api/common.types.ts:

/**
 * Standard paginated response wrapper.
 */
export interface PaginatedResponse<T> {
  data: T[];
  total: number;
  page: number;
  pageSize: number;
  totalPages: number;
}

/**
 * Standard pagination query parameters.
 */
export interface PaginationParams {
  page?: number;
  pageSize?: number;
}

/**
 * Standard API error response.
 */
export interface ApiErrorResponse {
  statusCode: number;
  message: string;
  error?: string;
  details?: Record<string, unknown>;
  timestamp?: string;
  path?: string;
}

/**
 * Standard success response for mutations.
 */
export interface MutationResponse<T = void> {
  success: boolean;
  data?: T;
  message?: string;
}

3. Create Order API Types

Create libs/domain-contracts/src/api/order.api.ts:

import { OrderStatus, LineItemStatus } from '../lib/types';
import { ShippingAddress } from '@forma3d/domain';
import { PaginationParams, PaginatedResponse } from './common.types';

/**
 * Line item within an order response.
 */
export interface LineItemApiResponse {
  id: string;
  orderId: string;
  shopifyLineItemId: string;
  productMappingId: string | null;
  sku: string;
  productName: string;
  quantity: number;
  unitPrice: string;
  totalPrice: string;
  status: LineItemStatus;
  requiresPrinting: boolean;
  completedParts: number;
  totalParts: number;
  createdAt: string;
  updatedAt: string;
}

/**
 * Full order response from API.
 */
export interface OrderApiResponse {
  id: string;
  shopifyOrderId: string;
  shopifyOrderNumber: string;
  status: OrderStatus;
  customerName: string;
  customerEmail: string;
  shippingAddress: ShippingAddress | null;
  totalPrice: string;
  currency: string;
  totalParts: number;
  completedParts: number;
  notes: string | null;
  createdAt: string;
  updatedAt: string;
  completedAt: string | null;
  lineItems: LineItemApiResponse[];
}

/**
 * Order list item (without nested line items).
 */
export interface OrderListItemApiResponse {
  id: string;
  shopifyOrderId: string;
  shopifyOrderNumber: string;
  status: OrderStatus;
  customerName: string;
  customerEmail: string;
  totalPrice: string;
  currency: string;
  totalParts: number;
  completedParts: number;
  createdAt: string;
  updatedAt: string;
  completedAt: string | null;
}

/**
 * Order list query parameters.
 */
export interface OrderListParams extends PaginationParams {
  status?: OrderStatus;
  search?: string;
  sortBy?: 'createdAt' | 'updatedAt' | 'shopifyOrderNumber';
  sortOrder?: 'asc' | 'desc';
}

/**
 * Paginated order list response.
 */
export interface OrderListApiResponse {
  orders: OrderListItemApiResponse[];
  total: number;
  page: number;
  pageSize: number;
}

/**
 * Update order status request.
 */
export interface UpdateOrderStatusRequest {
  status: OrderStatus;
}

4. Create Print Job API Types

Create libs/domain-contracts/src/api/print-job.api.ts:

import { PrintJobStatus } from '../lib/types';
import { PaginationParams } from './common.types';

/**
 * Print job response from API.
 */
export interface PrintJobApiResponse {
  id: string;
  lineItemId: string;
  assemblyPartId: string | null;
  simplyPrintJobId: string | null;
  status: PrintJobStatus;
  copyNumber: number;
  printerId: string | null;
  printerName: string | null;
  fileId: string | null;
  fileName: string | null;
  queuedAt: string | null;
  startedAt: string | null;
  completedAt: string | null;
  estimatedDuration: number | null;
  actualDuration: number | null;
  progress: number;
  errorMessage: string | null;
  retryCount: number;
  maxRetries: number;
  createdAt: string;
  updatedAt: string;
  // Joined fields
  orderId: string;
  shopifyOrderNumber: string;
  productSku: string;
  productName: string;
}

/**
 * Print job list query parameters.
 */
export interface PrintJobListParams extends PaginationParams {
  status?: PrintJobStatus;
  orderId?: string;
  printerId?: string;
}

/**
 * Paginated print job list response.
 */
export interface PrintJobListApiResponse {
  data: PrintJobApiResponse[];
  total: number;
  page: number;
  pageSize: number;
}

/**
 * Cancel print job request.
 */
export interface CancelPrintJobRequest {
  reason?: string;
}

5. Create Dashboard API Types

Create libs/domain-contracts/src/api/dashboard.api.ts:

/**
 * Dashboard statistics response.
 */
export interface DashboardStatsApiResponse {
  pendingOrders: number;
  processingOrders: number;
  completedToday: number;
  failedOrders: number;
  activePrintJobs: number;
  completedPrintJobsToday: number;
  queuedPrintJobs: number;
  failedPrintJobs: number;
  printersOnline: number;
  averagePrintTime: number | null;
}

/**
 * Dashboard order summary.
 */
export interface DashboardOrderSummary {
  pending: number;
  processing: number;
  completed: number;
  failed: number;
  cancelled: number;
}

/**
 * Dashboard print job summary.
 */
export interface DashboardPrintJobSummary {
  queued: number;
  printing: number;
  completed: number;
  failed: number;
}

6. Create Product Mapping API Types

Create libs/domain-contracts/src/api/product-mapping.api.ts:

import { PrintProfile } from '@forma3d/domain';
import { PaginationParams } from './common.types';

/**
 * Assembly part within a product mapping.
 */
export interface AssemblyPartApiResponse {
  id: string;
  productMappingId: string;
  name: string;
  modelFileId: string;
  modelFileName: string;
  quantity: number;
  printProfile: PrintProfile | null;
  sortOrder: number;
  createdAt: string;
  updatedAt: string;
}

/**
 * Product mapping response from API.
 */
export interface ProductMappingApiResponse {
  id: string;
  shopifyProductId: string;
  shopifyVariantId: string | null;
  sku: string;
  productName: string;
  description: string | null;
  isAssembly: boolean;
  isActive: boolean;
  modelFileId: string | null;
  modelFileName: string | null;
  defaultPrintProfile: PrintProfile | null;
  createdAt: string;
  updatedAt: string;
  assemblyParts: AssemblyPartApiResponse[];
}

/**
 * Product mapping list query parameters.
 */
export interface ProductMappingListParams extends PaginationParams {
  isActive?: boolean;
  isAssembly?: boolean;
  search?: string;
}

/**
 * Paginated product mapping list response.
 */
export interface ProductMappingListApiResponse {
  mappings: ProductMappingApiResponse[];
  total: number;
  page: number;
  pageSize: number;
}

/**
 * Create/update product mapping request.
 */
export interface CreateProductMappingRequest {
  shopifyProductId: string;
  shopifyVariantId?: string;
  sku: string;
  productName: string;
  description?: string;
  isAssembly?: boolean;
  modelFileId?: string;
  modelFileName?: string;
  defaultPrintProfile?: PrintProfile;
}

export interface UpdateProductMappingRequest extends Partial<CreateProductMappingRequest> {
  isActive?: boolean;
}

7. Create Shipment API Types

Create libs/domain-contracts/src/api/shipment.api.ts:

import { ShipmentStatus } from '../lib/types';

/**
 * Shipment response from API.
 */
export interface ShipmentApiResponse {
  id: string;
  orderId: string;
  sendcloudParcelId: string | null;
  status: ShipmentStatus;
  carrier: string | null;
  trackingNumber: string | null;
  trackingUrl: string | null;
  labelUrl: string | null;
  weight: number | null;
  shippedAt: string | null;
  deliveredAt: string | null;
  createdAt: string;
  updatedAt: string;
}

/**
 * Shipping integration status.
 */
export interface ShippingStatusApiResponse {
  enabled: boolean;
  provider: 'sendcloud' | 'manual' | null;
  configured: boolean;
}

/**
 * Create shipping label request.
 */
export interface CreateShippingLabelRequest {
  orderId: string;
  weight?: number;
  carrier?: string;
}

8. Create Event Log API Types

Create libs/domain-contracts/src/api/event-log.api.ts:

import { EventMetadata } from '@forma3d/domain';
import { PaginationParams } from './common.types';

/**
 * Event severity levels.
 */
export type EventSeverity = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR';

/**
 * Event log entry response.
 */
export interface EventLogApiResponse {
  id: string;
  orderId: string | null;
  printJobId: string | null;
  eventType: string;
  severity: EventSeverity;
  message: string;
  metadata: EventMetadata | null;
  createdAt: string;
}

/**
 * Event log list query parameters.
 */
export interface EventLogListParams extends PaginationParams {
  orderId?: string;
  printJobId?: string;
  severity?: EventSeverity;
  eventType?: string;
  startDate?: string;
  endDate?: string;
}

/**
 * Paginated event log list response.
 */
export interface EventLogListApiResponse {
  logs: EventLogApiResponse[];
  total: number;
  page: number;
  pageSize: number;
}

9. Create Health API Types

Create libs/domain-contracts/src/api/health.api.ts:

/**
 * Health check response.
 */
export interface HealthApiResponse {
  status: 'ok' | 'degraded' | 'unhealthy';
  timestamp: string;
  version?: string;
}

/**
 * Detailed health check response.
 */
export interface HealthDetailedApiResponse extends HealthApiResponse {
  database: 'connected' | 'disconnected';
  redis?: 'connected' | 'disconnected';
  simplyPrint?: 'connected' | 'disconnected' | 'unconfigured';
  sendcloud?: 'connected' | 'disconnected' | 'unconfigured';
  uptime: number;
  memoryUsage: {
    heapUsed: number;
    heapTotal: number;
    rss: number;
  };
}

10. Create API Index

Create libs/domain-contracts/src/api/index.ts:

// Common types
export * from './common.types';

// Resource-specific types
export * from './order.api';
export * from './print-job.api';
export * from './dashboard.api';
export * from './product-mapping.api';
export * from './shipment.api';
export * from './event-log.api';
export * from './health.api';

Update libs/domain-contracts/src/index.ts:

export * from './lib/types';
export * from './api';

Phase 2: Update Frontend API Client (4 hours)

Priority: High | Impact: High | Dependencies: Phase 1

1. Update API Client to Use Shared Types

Update apps/web/src/lib/api-client.ts:

import {
  // Order types
  OrderApiResponse,
  OrderListApiResponse,
  OrderListParams,
  UpdateOrderStatusRequest,
  // Print job types
  PrintJobApiResponse,
  PrintJobListApiResponse,
  PrintJobListParams,
  CancelPrintJobRequest,
  // Dashboard types
  DashboardStatsApiResponse,
  // Product mapping types
  ProductMappingApiResponse,
  ProductMappingListApiResponse,
  ProductMappingListParams,
  CreateProductMappingRequest,
  UpdateProductMappingRequest,
  // Shipment types
  ShipmentApiResponse,
  ShippingStatusApiResponse,
  CreateShippingLabelRequest,
  // Event log types
  EventLogListApiResponse,
  EventLogListParams,
  // Health types
  HealthApiResponse,
  HealthDetailedApiResponse,
} from '@forma3d/domain-contracts';

const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:3000';

// Remove all local interface definitions - now imported from shared types

async function request<T>(
  endpoint: string,
  options: RequestInit = {},
  apiKey?: string,
): Promise<T> {
  const headers: HeadersInit = {
    'Content-Type': 'application/json',
    ...options.headers,
  };

  if (apiKey) {
    (headers as Record<string, string>)['X-API-Key'] = apiKey;
  }

  const response = await fetch(`${API_BASE}${endpoint}`, {
    ...options,
    headers,
  });

  if (!response.ok) {
    const error = await response.json().catch(() => ({ message: 'Request failed' }));
    throw new Error(error.message || `HTTP ${response.status}`);
  }

  if (response.status === 204) {
    return undefined as T;
  }

  return response.json();
}

export const apiClient = {
  health: {
    check: () => request<HealthApiResponse>('/health'),
    live: () => request<HealthApiResponse>('/health/live'),
    ready: () => request<HealthDetailedApiResponse>('/health/ready'),
  },

  orders: {
    list: (params?: OrderListParams) => {
      const query = new URLSearchParams();
      if (params?.page) query.set('page', String(params.page));
      if (params?.pageSize) query.set('pageSize', String(params.pageSize));
      if (params?.status) query.set('status', params.status);
      if (params?.search) query.set('search', params.search);
      const queryString = query.toString();
      return request<OrderListApiResponse>(
        `/api/v1/orders${queryString ? `?${queryString}` : ''}`,
      );
    },
    get: (id: string) => request<OrderApiResponse>(`/api/v1/orders/${id}`),
    updateStatus: (id: string, status: UpdateOrderStatusRequest['status'], apiKey: string) =>
      request<OrderApiResponse>(
        `/api/v1/orders/${id}/status`,
        { method: 'PUT', body: JSON.stringify({ status }) },
        apiKey,
      ),
    cancel: (id: string, apiKey: string) =>
      request<OrderApiResponse>(
        `/api/v1/orders/${id}/cancel`,
        { method: 'PUT' },
        apiKey,
      ),
  },

  printJobs: {
    list: (params?: PrintJobListParams) => {
      const query = new URLSearchParams();
      if (params?.page) query.set('page', String(params.page));
      if (params?.pageSize) query.set('pageSize', String(params.pageSize));
      if (params?.status) query.set('status', params.status);
      if (params?.orderId) query.set('orderId', params.orderId);
      const queryString = query.toString();
      return request<PrintJobListApiResponse>(
        `/api/v1/print-jobs${queryString ? `?${queryString}` : ''}`,
      );
    },
    getByOrderId: (orderId: string) =>
      request<PrintJobApiResponse[]>(`/api/v1/print-jobs/order/${orderId}`),
    getActive: () => request<PrintJobApiResponse[]>('/api/v1/print-jobs/active'),
    retry: (id: string, apiKey: string) =>
      request<PrintJobApiResponse>(
        `/api/v1/print-jobs/${id}/retry`,
        { method: 'POST' },
        apiKey,
      ),
    cancel: (id: string, reason: string | undefined, apiKey: string) =>
      request<PrintJobApiResponse>(
        `/api/v1/print-jobs/${id}/cancel`,
        { method: 'POST', body: JSON.stringify({ reason } satisfies CancelPrintJobRequest) },
        apiKey,
      ),
  },

  dashboard: {
    getStats: () => request<DashboardStatsApiResponse>('/api/v1/dashboard/stats'),
  },

  mappings: {
    list: (params?: ProductMappingListParams) => {
      const query = new URLSearchParams();
      if (params?.page) query.set('page', String(params.page));
      if (params?.pageSize) query.set('pageSize', String(params.pageSize));
      if (params?.isActive !== undefined) query.set('isActive', String(params.isActive));
      if (params?.search) query.set('search', params.search);
      const queryString = query.toString();
      return request<ProductMappingListApiResponse>(
        `/api/v1/product-mappings${queryString ? `?${queryString}` : ''}`,
      );
    },
    get: (id: string) => request<ProductMappingApiResponse>(`/api/v1/product-mappings/${id}`),
    create: (data: CreateProductMappingRequest, apiKey: string) =>
      request<ProductMappingApiResponse>(
        '/api/v1/product-mappings',
        { method: 'POST', body: JSON.stringify(data) },
        apiKey,
      ),
    update: (id: string, data: UpdateProductMappingRequest, apiKey: string) =>
      request<ProductMappingApiResponse>(
        `/api/v1/product-mappings/${id}`,
        { method: 'PUT', body: JSON.stringify(data) },
        apiKey,
      ),
    delete: (id: string, apiKey: string) =>
      request<void>(
        `/api/v1/product-mappings/${id}`,
        { method: 'DELETE' },
        apiKey,
      ),
  },

  logs: {
    list: (params?: EventLogListParams) => {
      const query = new URLSearchParams();
      if (params?.page) query.set('page', String(params.page));
      if (params?.pageSize) query.set('pageSize', String(params.pageSize));
      if (params?.orderId) query.set('orderId', params.orderId);
      if (params?.severity) query.set('severity', params.severity);
      if (params?.eventType) query.set('eventType', params.eventType);
      const queryString = query.toString();
      return request<EventLogListApiResponse>(
        `/api/v1/logs${queryString ? `?${queryString}` : ''}`,
      );
    },
  },

  shipping: {
    getByOrderId: async (orderId: string) => {
      try {
        return await request<ShipmentApiResponse>(`/api/v1/shipments/order/${orderId}`);
      } catch {
        return null;
      }
    },
    getStatus: () => request<ShippingStatusApiResponse>('/api/v1/shipping/status'),
    createLabel: (data: CreateShippingLabelRequest, apiKey: string) =>
      request<ShipmentApiResponse>(
        '/api/v1/shipping/label',
        { method: 'POST', body: JSON.stringify(data) },
        apiKey,
      ),
  },
};

2. Update Frontend Hooks to Use Shared Types

Update hooks to import types from @forma3d/domain-contracts instead of local definitions.

Example update for apps/web/src/hooks/use-orders.ts:

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import {
  OrderApiResponse,
  OrderListApiResponse,
  OrderListParams,
  PrintJobApiResponse,
} from '@forma3d/domain-contracts';
import { apiClient } from '../lib/api-client';
import { useAuth } from '../contexts/auth-context';

export function useOrders(params?: OrderListParams) {
  return useQuery<OrderListApiResponse>({
    queryKey: ['orders', params],
    queryFn: () => apiClient.orders.list(params),
  });
}

export function useOrder(id: string) {
  return useQuery<OrderApiResponse>({
    queryKey: ['order', id],
    queryFn: () => apiClient.orders.get(id),
    enabled: !!id,
  });
}

// ... rest of hooks using imported types

Phase 3: Update Backend DTOs to Extend Shared Types (4 hours)

Priority: High | Impact: High | Dependencies: Phase 1

1. Update Order DTOs

Update apps/api/src/orders/dto/order.dto.ts:

import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import {
  OrderApiResponse,
  OrderListItemApiResponse,
  LineItemApiResponse,
} from '@forma3d/domain-contracts';
import { ShippingAddress } from '@forma3d/domain';

/**
 * Line item DTO - implements shared interface.
 */
export class LineItemDto implements LineItemApiResponse {
  @ApiProperty()
  id!: string;

  @ApiProperty()
  orderId!: string;

  @ApiProperty()
  shopifyLineItemId!: string;

  @ApiPropertyOptional()
  productMappingId!: string | null;

  @ApiProperty()
  sku!: string;

  @ApiProperty()
  productName!: string;

  @ApiProperty()
  quantity!: number;

  @ApiProperty()
  unitPrice!: string;

  @ApiProperty()
  totalPrice!: string;

  @ApiProperty()
  status!: string;

  @ApiProperty()
  requiresPrinting!: boolean;

  @ApiProperty()
  completedParts!: number;

  @ApiProperty()
  totalParts!: number;

  @ApiProperty()
  createdAt!: string;

  @ApiProperty()
  updatedAt!: string;
}

/**
 * Order DTO - implements shared interface.
 */
export class OrderDto implements OrderApiResponse {
  @ApiProperty()
  id!: string;

  @ApiProperty()
  shopifyOrderId!: string;

  @ApiProperty()
  shopifyOrderNumber!: string;

  @ApiProperty()
  status!: string;

  @ApiProperty()
  customerName!: string;

  @ApiProperty()
  customerEmail!: string;

  @ApiPropertyOptional()
  shippingAddress!: ShippingAddress | null;

  @ApiProperty()
  totalPrice!: string;

  @ApiProperty()
  currency!: string;

  @ApiProperty()
  totalParts!: number;

  @ApiProperty()
  completedParts!: number;

  @ApiPropertyOptional()
  notes!: string | null;

  @ApiProperty()
  createdAt!: string;

  @ApiProperty()
  updatedAt!: string;

  @ApiPropertyOptional()
  completedAt!: string | null;

  @ApiProperty({ type: [LineItemDto] })
  lineItems!: LineItemApiResponse[];
}

Phase 4: Documentation Updates (30 minutes)

Priority: Medium | Impact: Medium | Dependencies: Phase 3

1. Update Technical Debt Register

Update docs/04-development/techdebt/technical-debt-register.md:

### ~~TD-004: Duplicated DTO Definitions Between Frontend and Backend~~ ✅ RESOLVED

**Type:** Architecture Debt  
**Status:****Resolved in Phase 5f**  
**Resolution Date:** 2026-XX-XX

#### Resolution

Created shared API types in `libs/domain-contracts` used by both frontend and backend:

- **Shared Types**: All API response/request types in `libs/domain-contracts/src/api/`
- **Frontend**: API client imports from shared types
- **Backend**: DTOs implement shared interfaces
- **Type Safety**: Compile-time checking across the full stack

**Files Created:**
- `libs/domain-contracts/src/api/common.types.ts`
- `libs/domain-contracts/src/api/order.api.ts`
- `libs/domain-contracts/src/api/print-job.api.ts`
- `libs/domain-contracts/src/api/dashboard.api.ts`
- `libs/domain-contracts/src/api/product-mapping.api.ts`
- `libs/domain-contracts/src/api/shipment.api.ts`
- `libs/domain-contracts/src/api/event-log.api.ts`
- `libs/domain-contracts/src/api/health.api.ts`

**Files Modified:**
- `apps/web/src/lib/api-client.ts` - Uses shared types
- `apps/web/src/hooks/*.ts` - Import shared types
- `apps/api/src/**/dto/*.ts` - Implement shared interfaces

📁 Files to Create/Modify

New Files

libs/domain-contracts/src/api/
  index.ts
  common.types.ts
  order.api.ts
  print-job.api.ts
  dashboard.api.ts
  product-mapping.api.ts
  shipment.api.ts
  event-log.api.ts
  health.api.ts

Modified Files

libs/domain-contracts/src/index.ts                # Export API types
apps/web/src/lib/api-client.ts                    # Use shared types
apps/web/src/hooks/use-orders.ts                  # Import shared types
apps/web/src/hooks/use-dashboard.ts
apps/web/src/hooks/use-mappings.ts
apps/web/src/hooks/use-logs.ts
apps/web/src/hooks/use-shipments.ts
apps/web/src/hooks/use-health.ts
apps/api/src/orders/dto/order.dto.ts              # Implement interfaces
apps/api/src/print-jobs/dto/print-job.dto.ts
apps/api/src/dashboard/dto/dashboard.dto.ts
docs/04-development/techdebt/technical-debt-register.md

✅ Validation Checklist

Phase 1: Shared Types

  • Common pagination types created
  • Order API types created
  • Print job API types created
  • Dashboard API types created
  • Product mapping API types created
  • Shipment API types created
  • Event log API types created
  • Health API types created
  • All types exported from domain-contracts

Phase 2: Frontend Integration

  • API client updated to use shared types
  • All local interface definitions removed
  • Hooks updated to import shared types
  • Frontend builds without type errors

Phase 3: Backend Integration

  • DTOs implement shared interfaces
  • Swagger decorators preserved
  • Backend builds without type errors

Final Verification

# All builds pass
pnpm nx build domain-contracts
pnpm nx build web
pnpm nx build api

# Type checking
pnpm nx lint domain-contracts
pnpm nx lint web
pnpm nx lint api

# Tests pass
pnpm nx test web
pnpm nx test api

🚫 Constraints and Rules

MUST DO

  • Create shared types in libs/domain-contracts
  • Export all types from package index
  • Update frontend to use shared types
  • Make backend DTOs implement shared interfaces
  • Maintain Swagger decorators on DTOs

MUST NOT

  • Duplicate type definitions
  • Use any for API responses
  • Skip nullable fields
  • Change API response shapes
  • Break existing API contracts

END OF PROMPT


This prompt resolves TD-004 from the technical debt register by creating shared API types in libs/domain-contracts used by both frontend and backend.