git init
This commit is contained in:
254
backend/architecture.md
Normal file
254
backend/architecture.md
Normal file
@@ -0,0 +1,254 @@
|
||||
# Backend Architecture
|
||||
|
||||
Backend stack:
|
||||
|
||||
- Node.js
|
||||
- TypeScript
|
||||
- NestJS
|
||||
- Prisma ORM
|
||||
- PostgreSQL
|
||||
|
||||
The backend is generated from the DSL specification.
|
||||
|
||||
Each DSL entity becomes:
|
||||
|
||||
- Prisma model
|
||||
- NestJS module
|
||||
- CRUD controller
|
||||
- Service
|
||||
- DTO definitions
|
||||
|
||||
---
|
||||
|
||||
# Project Structure
|
||||
|
||||
server/
|
||||
package.json
|
||||
|
||||
prisma/
|
||||
schema.prisma
|
||||
|
||||
src/
|
||||
main.ts
|
||||
app.module.ts
|
||||
|
||||
modules/
|
||||
|
||||
{entity}/
|
||||
{entity}.module.ts
|
||||
{entity}.controller.ts
|
||||
{entity}.service.ts
|
||||
|
||||
dto/
|
||||
create-{entity}.dto.ts
|
||||
update-{entity}.dto.ts
|
||||
{entity}.response.dto.ts
|
||||
|
||||
---
|
||||
|
||||
# Module Rules
|
||||
|
||||
Each entity generates exactly one NestJS module.
|
||||
|
||||
Example:
|
||||
|
||||
Entity:
|
||||
Equipment
|
||||
|
||||
Module:
|
||||
|
||||
modules/
|
||||
equipment/
|
||||
equipment.module.ts
|
||||
equipment.controller.ts
|
||||
equipment.service.ts
|
||||
|
||||
---
|
||||
|
||||
# Controller Rules
|
||||
|
||||
Each entity controller must expose these endpoints:
|
||||
|
||||
- GET /{resource} — list
|
||||
- GET /{resource}/:pk — get one
|
||||
- POST /{resource} — create
|
||||
- PATCH /{resource}/:pk — update
|
||||
- DELETE /{resource}/:pk — delete
|
||||
|
||||
The path parameter **:pk** must use the **primary key attribute name** from the DSL, not always `:id`.
|
||||
|
||||
## Path parameter by primary key
|
||||
|
||||
| Entity | Primary key (DSL) | Path parameter | Example routes |
|
||||
| ------------- | ----------------- | -------------- | -------------------------------------------------------- |
|
||||
| Equipment | id | :id | GET /equipment/:id, PATCH /equipment/:id |
|
||||
| EquipmentType | code | :code | GET /equipment-types/:code, PATCH /equipment-types/:code |
|
||||
| RepairOrder | id | :id | GET /repair-orders/:id, PATCH /repair-orders/:id |
|
||||
|
||||
**Rule:** Use the actual primary key name. For example, **EquipmentType** has PK `code`, so routes must be:
|
||||
|
||||
- GET /equipment-types/:code
|
||||
- PATCH /equipment-types/:code
|
||||
- DELETE /equipment-types/:code
|
||||
|
||||
**Do not** use `/equipment-types/:id` when the entity primary key is `code`.
|
||||
|
||||
---
|
||||
|
||||
# Health Endpoint
|
||||
|
||||
Every generated backend must expose a **health endpoint** so that runtime and orchestration can verify the API is up.
|
||||
|
||||
- **Path:** `GET /health`
|
||||
- **Response:** JSON with a status indicator (e.g. `{ "status": "ok" }`).
|
||||
|
||||
## Controller example
|
||||
|
||||
```typescript
|
||||
@Controller("health")
|
||||
export class HealthController {
|
||||
@Get()
|
||||
getHealth() {
|
||||
return { status: "ok" };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Register the health controller in the root app module (or a dedicated health module). No authentication required for this endpoint.
|
||||
|
||||
---
|
||||
|
||||
# List Endpoint
|
||||
|
||||
List endpoint must support pagination and filters via query parameters.
|
||||
|
||||
Example:
|
||||
|
||||
GET /equipment?page=0&size=10
|
||||
|
||||
Response format must follow React Admin requirements:
|
||||
|
||||
{
|
||||
"data": [],
|
||||
"total": number
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
# Service Layer
|
||||
|
||||
Services implement CRUD operations using Prisma.
|
||||
|
||||
Example:
|
||||
|
||||
findAll()
|
||||
findOne(id)
|
||||
create(data)
|
||||
update(id, data)
|
||||
remove(id)
|
||||
|
||||
---
|
||||
|
||||
# DTO Rules
|
||||
|
||||
Create DTO:
|
||||
|
||||
- contains required fields
|
||||
- does NOT contain generated primary keys
|
||||
|
||||
Update DTO:
|
||||
|
||||
- all fields optional
|
||||
|
||||
Response DTO:
|
||||
|
||||
- mirrors domain entity attributes
|
||||
|
||||
---
|
||||
|
||||
# Naming Conventions
|
||||
|
||||
## Entity naming
|
||||
|
||||
DSL entities use PascalCase. Generated backend artifacts use the same base name in lowercase for folders and file prefixes.
|
||||
|
||||
- **Equipment** → equipment module
|
||||
- **EquipmentType** → equipment-type module (or equipment-type as path segment)
|
||||
- **RepairOrder** → repair-order module
|
||||
|
||||
## Module naming
|
||||
|
||||
One entity = one module folder. Folder name = entity name in kebab-case (lowercase, hyphen-separated).
|
||||
|
||||
- Equipment → `modules/equipment/`
|
||||
- EquipmentType → `modules/equipment-type/`
|
||||
- RepairOrder → `modules/repair-order/`
|
||||
|
||||
## Controller naming
|
||||
|
||||
- File: `{entity-kebab}.controller.ts`
|
||||
- Class: `EquipmentController`, `EquipmentTypeController`, `RepairOrderController`
|
||||
|
||||
Examples:
|
||||
|
||||
- `equipment.controller.ts`
|
||||
- `equipment-type.controller.ts`
|
||||
- `repair-order.controller.ts`
|
||||
|
||||
## Service naming
|
||||
|
||||
- File: `{entity-kebab}.service.ts`
|
||||
- Class: `EquipmentService`, `EquipmentTypeService`, `RepairOrderService`
|
||||
|
||||
Examples:
|
||||
|
||||
- `equipment.service.ts`
|
||||
- `equipment-type.service.ts`
|
||||
- `repair-order.service.ts`
|
||||
|
||||
## DTO naming
|
||||
|
||||
- Create: `create-{entity-kebab}.dto.ts` (e.g. `create-equipment.dto.ts`, `create-repair-order.dto.ts`)
|
||||
- Update: `update-{entity-kebab}.dto.ts` (e.g. `update-equipment.dto.ts`)
|
||||
- Response: `{entity-kebab}.response.dto.ts` or use entity name for list/detail response types
|
||||
|
||||
---
|
||||
|
||||
# Resource Naming Rules
|
||||
|
||||
API resource paths are derived from the entity name:
|
||||
|
||||
1. **PascalCase → kebab-case:** Replace camelCase with lowercase hyphenated segments.
|
||||
2. **Pluralize:** Use plural form for the resource path (list endpoint represents a collection).
|
||||
|
||||
| Entity (DSL) | API path (resource) |
|
||||
| ------------- | ------------------- |
|
||||
| Equipment | /equipment |
|
||||
| EquipmentType | /equipment-types |
|
||||
| RepairOrder | /repair-orders |
|
||||
|
||||
Rules:
|
||||
|
||||
- **Equipment** → `equipment` (already singular-looking; path is still `/equipment` for consistency with REST resource naming).
|
||||
- **EquipmentType** → `equipment-types` (camelCase "EquipmentType" → "equipment-type", then plural → "equipment-types").
|
||||
- **RepairOrder** → `repair-orders` ("RepairOrder" → "repair-order" → "repair-orders").
|
||||
|
||||
Standard endpoints per resource:
|
||||
|
||||
- GET /{resource} — list
|
||||
- GET /{resource}/:pk — get one (pk = primary key name, e.g. :id or :code)
|
||||
- POST /{resource} — create
|
||||
- PATCH /{resource}/:pk — update
|
||||
- DELETE /{resource}/:pk — delete
|
||||
|
||||
See **Controller Rules** above for the rule that :pk must match the entity's primary key attribute name.
|
||||
|
||||
---
|
||||
|
||||
# Environment and runtime
|
||||
|
||||
- **Environment variables:** Backend requires at least `DATABASE_URL`. See **backend/runtime-rules.md**.
|
||||
- **.env:** Generated project must include a `.env` (and `.env.example`) with `DATABASE_URL` so the app starts without runtime errors.
|
||||
- **PrismaService:** Must follow **backend/prisma-service.md** (OnModuleInit, $connect; no beforeExit).
|
||||
- **Prisma client:** Add `"postinstall": "prisma generate"` (or equivalent) to package.json so the client is generated after install.
|
||||
- **Migrations:** Document or run `npx prisma migrate dev` after schema generation. See **backend/runtime-rules.md** and **generation/backend-generation.md**.
|
||||
70
backend/database-runtime.md
Normal file
70
backend/database-runtime.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Database Runtime
|
||||
|
||||
The generated project must include a **PostgreSQL development database** so the application can run immediately after generation without manual database setup.
|
||||
|
||||
Use **Docker** to provision the database.
|
||||
|
||||
---
|
||||
|
||||
# docker-compose.yml
|
||||
|
||||
The generator must create a `docker-compose.yml` file at the **project root** (same level as `server/` and `client/` directories).
|
||||
|
||||
## Example
|
||||
|
||||
```yaml
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
container_name: toir-postgres
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: toir
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Rules
|
||||
|
||||
- **Location:** Project root (e.g. `TOiR-generation/docker-compose.yml` or monorepo root).
|
||||
- **Service name:** Can be `postgres` or project-specific (e.g. `toir-postgres` as container_name).
|
||||
- **Credentials and DB name** must match the `DATABASE_URL` in `server/.env`:
|
||||
- User: `postgres`
|
||||
- Password: `postgres`
|
||||
- Database: `toir`
|
||||
- Host: `localhost`
|
||||
- Port: `5432`
|
||||
- **Volume:** Use a named volume so data persists across container restarts.
|
||||
|
||||
---
|
||||
|
||||
# Usage
|
||||
|
||||
Start the database before running the backend:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Stop:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
Without this file and a running database, the backend will fail at runtime with errors such as:
|
||||
|
||||
```
|
||||
PrismaClientInitializationError P1001: Can't reach database server at localhost:5432
|
||||
```
|
||||
166
backend/prisma-rules.md
Normal file
166
backend/prisma-rules.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# DSL → Prisma Mapping Rules
|
||||
|
||||
The domain DSL defines the database schema.
|
||||
|
||||
Each DSL entity becomes a Prisma model.
|
||||
|
||||
---
|
||||
|
||||
# Type Mapping (Prisma schema)
|
||||
|
||||
| DSL Type | Prisma Type |
|
||||
|--------|-------------|
|
||||
| string | String |
|
||||
| uuid | String @id @default(uuid()) |
|
||||
| integer | Int |
|
||||
| decimal | Decimal |
|
||||
| date | DateTime |
|
||||
| text | String |
|
||||
|
||||
---
|
||||
|
||||
# DTO Type Mapping (API / serialization)
|
||||
|
||||
For DTOs and API responses, use types that serialize to JSON without errors. Prisma's `Decimal` and `DateTime` do not always serialize predictably; map them as follows in generated DTOs:
|
||||
|
||||
| DSL Type | Prisma Type | DTO / API type | Notes |
|
||||
|----------|-------------|----------------|-------|
|
||||
| string | String | string | — |
|
||||
| uuid | String | string | — |
|
||||
| integer | Int | number | — |
|
||||
| decimal | Decimal | **string** | Avoid Prisma Decimal in DTO; use string to prevent serialization issues. |
|
||||
| date | DateTime | **string** | ISO 8601 date string (e.g. `"2025-03-08T00:00:00.000Z"`). |
|
||||
| text | String | string | — |
|
||||
| enum | enum | string | Enum value as string. |
|
||||
|
||||
**Rules:**
|
||||
- **decimal** → In create/update/response DTOs, use `string` (or a type that serializes to string). Convert to/from Prisma `Decimal` only in the service layer.
|
||||
- **date** → In DTOs, use `string` (ISO date). Convert to/from Prisma `DateTime` in the service layer.
|
||||
|
||||
---
|
||||
|
||||
# Enum Mapping
|
||||
|
||||
DSL enum becomes Prisma enum.
|
||||
|
||||
Example:
|
||||
|
||||
DSL
|
||||
|
||||
enum EquipmentStatus {
|
||||
Active
|
||||
Repair
|
||||
}
|
||||
|
||||
Prisma
|
||||
|
||||
enum EquipmentStatus {
|
||||
Active
|
||||
Repair
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
# Foreign Keys
|
||||
|
||||
DSL foreign keys become Prisma relations.
|
||||
|
||||
Example:
|
||||
|
||||
DSL
|
||||
|
||||
attribute equipmentTypeCode {
|
||||
type string;
|
||||
key foreign {
|
||||
relates EquipmentType.code;
|
||||
}
|
||||
}
|
||||
|
||||
Prisma
|
||||
|
||||
equipmentTypeCode String
|
||||
equipmentType EquipmentType @relation(fields: [equipmentTypeCode], references: [code])
|
||||
|
||||
---
|
||||
|
||||
# Primary Keys
|
||||
|
||||
Primary keys defined in DSL must become Prisma model identifiers.
|
||||
|
||||
Example:
|
||||
|
||||
attribute id {
|
||||
type uuid;
|
||||
key primary;
|
||||
}
|
||||
|
||||
Prisma
|
||||
|
||||
id String @id @default(uuid())
|
||||
|
||||
---
|
||||
|
||||
# Prisma Service Rules
|
||||
|
||||
Use Prisma v5 compatible service. See **backend/prisma-service.md** for full specification.
|
||||
|
||||
- PrismaService MUST implement `OnModuleInit` and call `await this.$connect()` in `onModuleInit()`.
|
||||
- PrismaService MUST NOT use the deprecated `this.$on('beforeExit', ...)` hook (not supported in Prisma 5).
|
||||
|
||||
---
|
||||
|
||||
# Prisma Lifecycle Rules
|
||||
|
||||
These lifecycle rules must be applied in generated projects so Prisma runtime is reliable after generation and after dependency installation.
|
||||
|
||||
## 1) Generate Prisma client after schema changes
|
||||
|
||||
Whenever `prisma/schema.prisma` is generated or updated, run:
|
||||
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
## 2) postinstall lifecycle
|
||||
|
||||
Generated `server/package.json` must include:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"postinstall": "prisma generate"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This ensures Prisma client generation after `npm install`.
|
||||
|
||||
## 3) Migration lifecycle
|
||||
|
||||
Use development migration workflow:
|
||||
|
||||
```bash
|
||||
npx prisma migrate dev
|
||||
```
|
||||
|
||||
Run this after database startup and before starting the backend server.
|
||||
|
||||
## 4) Seed lifecycle
|
||||
|
||||
If seed is configured (see `backend/seed-rules.md`), run:
|
||||
|
||||
```bash
|
||||
npx prisma db seed
|
||||
```
|
||||
|
||||
Generated `server/package.json` should include:
|
||||
|
||||
```json
|
||||
{
|
||||
"prisma": {
|
||||
"seed": "ts-node prisma/seed.ts"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use `tsx prisma/seed.ts` if the project standard uses `tsx` instead of `ts-node`.
|
||||
80
backend/prisma-service.md
Normal file
80
backend/prisma-service.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# PrismaService Implementation
|
||||
|
||||
The generated backend must provide a NestJS injectable service that wraps PrismaClient. This document defines the **only** supported implementation for Prisma 5+.
|
||||
|
||||
---
|
||||
|
||||
# Correct Implementation
|
||||
|
||||
```typescript
|
||||
import { Injectable, OnModuleInit } from "@nestjs/common";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
@Injectable()
|
||||
export class PrismaService extends PrismaClient implements OnModuleInit {
|
||||
async onModuleInit() {
|
||||
await this.$connect();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Why this pattern
|
||||
|
||||
- **OnModuleInit:** NestJS lifecycle hook; `onModuleInit()` runs when the module is initialized, ensuring the database connection is established before handling requests.
|
||||
- **$connect():** Explicitly connects the Prisma client. Required for reliable connection handling in serverless or long-running apps.
|
||||
|
||||
---
|
||||
|
||||
# Deprecated: Do NOT Use
|
||||
|
||||
The following pattern is **deprecated** in Prisma 5 and must **not** be generated:
|
||||
|
||||
```typescript
|
||||
// WRONG — do not generate
|
||||
this.$on("beforeExit", async () => {
|
||||
await this.$disconnect();
|
||||
});
|
||||
```
|
||||
|
||||
- `beforeExit` is not supported in Prisma 5.
|
||||
- Using it will cause runtime errors or warnings.
|
||||
|
||||
## Rule for generators
|
||||
|
||||
Generated `PrismaService` (or equivalent) must:
|
||||
|
||||
1. Extend `PrismaClient`.
|
||||
2. Implement `OnModuleInit`.
|
||||
3. Call `await this.$connect()` in `onModuleInit()`.
|
||||
4. **Not** use `this.$on('beforeExit', ...)` or any `beforeExit` hook.
|
||||
|
||||
---
|
||||
|
||||
# Module Registration
|
||||
|
||||
Register the service in a dedicated Prisma module (or in `AppModule`) so it can be injected into other services:
|
||||
|
||||
```typescript
|
||||
// prisma.module.ts (or app.module.ts)
|
||||
import { Global, Module } from "@nestjs/common";
|
||||
import { PrismaService } from "./prisma.service";
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [PrismaService],
|
||||
exports: [PrismaService],
|
||||
})
|
||||
export class PrismaModule {}
|
||||
```
|
||||
|
||||
Using `@Global()` allows injecting `PrismaService` in any module without re-importing.
|
||||
|
||||
---
|
||||
|
||||
# File location
|
||||
|
||||
Generated file:
|
||||
|
||||
- `src/prisma.prisma.service.ts` or `src/prisma.service.ts`
|
||||
|
||||
Class name: `PrismaService`.
|
||||
94
backend/runtime-rules.md
Normal file
94
backend/runtime-rules.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Backend Runtime Rules
|
||||
|
||||
This document defines runtime configuration requirements for the generated backend. Generators must produce a project that runs without errors when these rules are followed.
|
||||
|
||||
---
|
||||
|
||||
# Environment Variables
|
||||
|
||||
The backend **must** have a `.env` file at the project root (e.g. `server/.env` or backend package root). This file must **not** be committed with real credentials; provide an example instead (e.g. `.env.example`).
|
||||
|
||||
## Required variables
|
||||
|
||||
| Variable | Required | Description |
|
||||
| ------------ | -------- | --------------------------------------- |
|
||||
| DATABASE_URL | Yes | PostgreSQL connection string for Prisma |
|
||||
|
||||
## Example
|
||||
|
||||
```env
|
||||
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/toir"
|
||||
```
|
||||
|
||||
## Generation requirement
|
||||
|
||||
When generating the backend, **always** create:
|
||||
|
||||
1. **`.env.example`** — with placeholder DATABASE_URL and instructions.
|
||||
2. **`.env`** — with the same placeholder so the app can start; user replaces with real values.
|
||||
|
||||
If the generator does not create `.env`, the first run will fail with:
|
||||
|
||||
```
|
||||
Environment variable not found: DATABASE_URL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Prisma Client Generation
|
||||
|
||||
After the Prisma schema is generated or modified, the Prisma client must be generated.
|
||||
|
||||
## Command
|
||||
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
## Lifecycle rule
|
||||
|
||||
Add to generated `package.json` so that `prisma generate` runs after every `npm install`:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"postinstall": "prisma generate"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This ensures that after cloning or installing dependencies, the Prisma client is available without a manual step.
|
||||
|
||||
**Note:** Path may need to be adjusted if Prisma schema lives in a subfolder (e.g. `prisma generate` is typically run from the package root where `prisma/schema.prisma` exists).
|
||||
|
||||
---
|
||||
|
||||
# Database Migration
|
||||
|
||||
The generation process must document and support database migration.
|
||||
|
||||
## Command
|
||||
|
||||
```bash
|
||||
npx prisma migrate dev
|
||||
```
|
||||
|
||||
Run after:
|
||||
|
||||
1. Prisma schema has been generated or updated.
|
||||
2. `npx prisma generate` has been run.
|
||||
|
||||
## Generation requirement
|
||||
|
||||
1. Include migration in the backend generation pipeline (see `generation/backend-generation.md`).
|
||||
2. Document in README or post-generation validation that the user must run `npx prisma migrate dev` before first run (or provide a setup script that runs it).
|
||||
|
||||
---
|
||||
|
||||
# Summary
|
||||
|
||||
| Requirement | Action |
|
||||
| --------------- | ------------------------------------------------------------- |
|
||||
| DATABASE_URL | Create `.env` and `.env.example` with DATABASE_URL |
|
||||
| Prisma client | Run `npx prisma generate`; add `postinstall` script |
|
||||
| Database schema | Document/run `npx prisma migrate dev` after schema generation |
|
||||
82
backend/seed-rules.md
Normal file
82
backend/seed-rules.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Seed Data Rules
|
||||
|
||||
The generator must create a **Prisma seed script** so the development database contains minimal sample data. This allows the frontend and API to be used immediately after migration.
|
||||
|
||||
---
|
||||
|
||||
# Seed Script Location
|
||||
|
||||
**File:** `server/prisma/seed.ts`
|
||||
|
||||
(Or `server/prisma/seed.js` if the project uses JavaScript; TypeScript is preferred and may require `ts-node` or `tsx` to run.)
|
||||
|
||||
---
|
||||
|
||||
# Seed Data Requirements
|
||||
|
||||
The seed script must create **minimal sample data** for at least one record per main entity, so that:
|
||||
|
||||
- List views show data.
|
||||
- Reference fields (e.g. equipment type, equipment) have valid options.
|
||||
- The app is demo-ready without manual data entry.
|
||||
|
||||
## Example scope (TOiR-style domain)
|
||||
|
||||
- **One EquipmentType** (e.g. code `"pump"`, name `"Pump"`).
|
||||
- **One Equipment** (e.g. linked to that type, with required fields filled).
|
||||
- **One RepairOrder** (e.g. linked to that equipment, with required fields filled).
|
||||
|
||||
Order matters: create EquipmentType first, then Equipment (references type), then RepairOrder (references equipment). Use Prisma `create` with the generated client; respect unique constraints and foreign keys.
|
||||
|
||||
---
|
||||
|
||||
# Prisma Seed Configuration
|
||||
|
||||
The generator must add the following to **`server/package.json`**:
|
||||
|
||||
```json
|
||||
"prisma": {
|
||||
"seed": "ts-node prisma/seed.ts"
|
||||
}
|
||||
```
|
||||
|
||||
If the project uses a different runner (e.g. `tsx`), use that instead:
|
||||
|
||||
```json
|
||||
"prisma": {
|
||||
"seed": "tsx prisma/seed.ts"
|
||||
}
|
||||
```
|
||||
|
||||
Ensure the seed runner is installed (e.g. `ts-node` as dev dependency) so that:
|
||||
|
||||
```bash
|
||||
npx prisma db seed
|
||||
```
|
||||
|
||||
runs successfully.
|
||||
|
||||
---
|
||||
|
||||
# Running the Seed
|
||||
|
||||
After migrations:
|
||||
|
||||
```bash
|
||||
cd server
|
||||
npx prisma migrate dev
|
||||
npx prisma db seed
|
||||
```
|
||||
|
||||
Or document that the user can run `npx prisma db seed` once after the first migration to populate sample data.
|
||||
|
||||
---
|
||||
|
||||
# Summary
|
||||
|
||||
| Requirement | Action |
|
||||
|--------------------|--------|
|
||||
| Seed script | Create `server/prisma/seed.ts` (or equivalent). |
|
||||
| Sample data | At least one EquipmentType, one Equipment, one RepairOrder (or equivalent for the DSL). |
|
||||
| package.json | Add `"prisma": { "seed": "ts-node prisma/seed.ts" }` (or tsx). |
|
||||
| Seed runner | Ensure ts-node (or tsx) is available so `prisma db seed` works. |
|
||||
97
backend/service-rules.md
Normal file
97
backend/service-rules.md
Normal 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`. |
|
||||
Reference in New Issue
Block a user