This commit is contained in:
MaKarin
2026-03-15 17:29:37 +03:00
commit 33521016d3
86 changed files with 20514 additions and 0 deletions

97
backend/service-rules.md Normal file
View File

@@ -0,0 +1,97 @@
# Service Layer Rules
Generated NestJS services must follow these rules so that update operations work correctly with React Admin and Prisma.
---
# Update Payload Sanitization
React Admin (and many REST clients) **always send `id`** in PATCH/PUT request bodies.
Example payload from React Admin:
```json
{
"id": "003",
"name": "Pump"
}
```
Some entities use a **different primary key** (e.g. `code`). The API response includes `id` (mapped from the PK) for React Admin compatibility, but the Prisma model may have no `id` field—only `code`. If the service passes the incoming DTO directly to Prisma:
```typescript
// WRONG — causes runtime error
prisma.equipmentType.update({
where: { code },
data: dto // dto contains "id", which is not a Prisma field
});
```
Prisma throws because `id` (and possibly other non-updatable fields) are not on the model or must not be written in `data`.
---
# Rules
1. **Update payload must be sanitized** before passing to the ORM. Do not pass the raw request body as `data` to `prisma.*.update()`.
2. **Remove `id`** from the update payload. React Admin sends `id` for identity; it must not be written to the database as a column (unless the entity actually has an `id` column and it is intended to be immutable on update).
3. **Remove the primary key** field from the update payload. The primary key is used in `where`; it must not appear in `data`. For example, remove `code` from `data` when updating by `code`.
4. **Remove readonly attributes** (e.g. created timestamps, server-generated fields) if they are present in the DTO, so they are not passed to Prisma `data`.
---
# Example Implementation
Destructure identity and primary key (and any other non-updatable fields) out of the DTO, then pass only the rest as `data`:
**Entity with primary key `code` (e.g. EquipmentType):**
```typescript
update(code: string, dto: UpdateEquipmentTypeDto) {
const { id, code: _pk, ...data } = dto as any;
return this.prisma.equipmentType.update({
where: { code },
data,
});
}
```
Or, if the DTO type does not include `id` or `code`, explicitly omit only the fields that must not be written:
```typescript
update(code: string, dto: UpdateEquipmentTypeDto) {
const { id, code: _pk, ...data } = dto as Record<string, unknown>;
return this.prisma.equipmentType.update({
where: { code },
data,
});
}
```
**Entity with primary key `id` (e.g. Equipment):**
```typescript
update(id: string, dto: UpdateEquipmentDto) {
const { id: _pk, ...data } = dto as any;
return this.prisma.equipment.update({
where: { id },
data,
});
}
```
This way, `id` (and the PK) are never passed into `data`, and Prisma does not receive unknown or read-only fields.
---
# Summary
| Rule | Action |
|------|--------|
| Sanitize update payload | Before `prisma.*.update()`, strip non-data fields from the DTO. |
| Remove `id` | Do not pass `id` in `data` unless the entity has an updatable `id` (rare). |
| Remove primary key | Use PK only in `where`; omit from `data`. |
| Remove readonly fields | Omit created_at, server-only fields, etc. from `data`. |