Demo

RequestDto & ResponseDto

Every backend API body and response must use RequestDto<T> and ResponseDto<T> from @quan-erp/shared-backend-core. Never return raw payloads or accept unwrapped request bodies.

Overview

Wrappers give every endpoint the same shape: payload plus status, message, and a referenceId for tracing. Controllers read body.payload and return ResponseDto.ok / okWithEmpty / error.

  • Import from @quan-erp/shared-backend-core on the backend
  • Frontend uses the simplified types from @quan-erp/shared-frontend-core
  • Never return raw entities or accept bare DTOs on @Body()

1. ResponseDto<T>

Successful and error responses share one envelope.

  • ResponseDto.ok(data, options?) — success with payload
  • ResponseDto.okWithEmpty() — success with null payload (delete / no-content updates)
  • ResponseDto.error(message, code?, referenceId?, payload?) — error envelope
FieldTypeNotes
timestampstringISO time
status"success" | "error"Outcome flag
messagestring?User-facing message (localize when possible)
codeHttpStatus?Optional HTTP status for errors
referenceIdstringUUID for log / support tracing
payloadTActual data (or null for empty success)
TSXcontroller responses
1import { 2 Delete, 3 Get, 4 HttpStatus, 5 Post, 6 ResponseDto, 7} from "@quan-erp/shared-backend-core"; 8 9@Get("/profile") 10async getProfile() { 11 const profile = await this.service.getProfile(); 12 return ResponseDto.ok(profile); 13} 14 15@Delete("/history/:id") 16async deleteHistory() { 17 await this.service.delete(/* … */); 18 return ResponseDto.okWithEmpty(); 19} 20 21@Post("/update") 22async update() { 23 try { 24 return ResponseDto.ok( 25 { success: true }, 26 { message: "Profile updated successfully" }, 27 ); 28 } catch { 29 return ResponseDto.error( 30 "Failed to update profile", 31 HttpStatus.INTERNAL_SERVER_ERROR, 32 ); 33 } 34}

2. RequestDto<T>

@Body() expects RequestDto<T>. Client JSON wraps the real DTO; read it from body.payload.

TSXrequest body
1import { Body, Post, RequestDto } from "@quan-erp/shared-backend-core"; 2 3@Post("/calculate") 4async calculate(@Body() body: RequestDto<LoanCalculationDto>) { 5 const data = body.payload; 6 return ResponseDto.ok(await this.service.calculate(data)); 7}

3. Frontend pairing

On the client, wrap POST/PUT with new RequestDto(dto), cast response.data as ResponseDto<T>, check status, then return payload. Prefer shared-frontend-core helpers and Admin Axios & Query patterns.

  • new RequestDto(dto) before post/put
  • if (data.status === "error") throw …
  • GET fetchFn should return payload, not the full ResponseDto
  • See Frontend → Platform APIs → RequestDto & ResponseDto