# Backend Add Field

> Add a new field or value to an existing NestJS module: Prisma schema + migration, create/update/getAll DTOs, service create/update/select/include, and relation wiring. Use when extending an existing resource with a new column, enum, filter, or relationship.

- Skill: `xmuhameed/backend-add-field` (Agent Skill)
- Install (CLI): `npx skillmds@latest add xmuhameed/backend-add-field`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xmuhameed/backend-add-field/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: xmuhameed (https://skillmd.com/u/xmuhameed)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/xmuhameed/backend-add-field

---


# Add Field to Existing Module

**Not** for new modules — use `@backend-nest/create-module`.

**Reference:** Find the target module in `src/modules/{feature}/` and its Prisma model first.

## Checklist

```
[ ] 1. Prisma — add field (+ relation/junction if needed) + migrate
[ ] 2. Create DTO — required/optional field decorator
[ ] 3. Update DTO — optional version of same field
[ ] 4. GetAll DTO — only if field is a list filter
[ ] 5. Service — create, update, getAll where, select/include
[ ] 6. Relations — connect/disconnect, junction cleanup on delete
[ ] 7. Search — add to where.OR if text-searchable
[ ] 8. Verify barrel export in dto/index.ts unchanged
```

## Step 1 — Prisma

Add the field to the existing model in `schema.prisma`:

```prisma
model product {
  // ...existing fields
  sku         String?   // new optional string
  isFeatured  Boolean   @default(false)
  typeId      Int?      // new FK
  type        type?     @relation(fields: [typeId], references: [id])
}
```

| Field type | Prisma pattern |
|------------|----------------|
| Optional string/number/boolean | `String?`, `Int?`, `Boolean?` |
| Required with default | `Boolean @default(false)`, `Int @default(0)` |
| Enum | `status ProductStatus @default(DRAFT)` + `enum ProductStatus { ... }` |
| FK (many-to-one) | `{name}Id Int?` + relation field |
| Many-to-many | junction model e.g. `product_category` |

```bash
npx prisma migrate dev --name add-product-sku-and-featured
```

See `@backend-nest/prisma-database` for ID types, soft delete, indexes.

## Step 2 — DTOs

Use global decorators — see `@backend-nest/global-dtos`.

**Create DTO** — add the new field:

```typescript
export class CreateProductDto {
  // ...existing fields
  @OptionalStringField('Stock keeping unit') sku?: string;
  @OptionalBooleanField() isFeatured?: boolean;
  @OptionalNumberField() typeId?: number;
  @OptionalEnumField(ProductStatus) status?: ProductStatus;
}
```

**Update DTO** — mirror create with optional decorators:

```typescript
@OptionalStringField() sku?: string;
@OptionalBooleanField() isFeatured?: boolean;
@OptionalNumberField() typeId?: number;
```

**GetAll DTO** — only when the field is a filter on list endpoints:

```typescript
export class GetAllProductsDto {
  @OptionalBooleanField() isFeatured?: boolean;
  @OptionalNumberField() typeId?: number;
  @OptionalEnumField(ProductStatus) status?: ProductStatus;
}
```

## Step 3 — Service

### create

Spread scalar fields from DTO. Handle relations separately:

```typescript
const created = await this.prisma.product.create({
  data: {
    ...dto,
    typeId: undefined, // strip FK before spread if using connect
    type: dto.typeId ? { connect: { id: dto.typeId } } : undefined,
  },
});
```

### update

Build a `data` object — only include provided fields:

```typescript
const data: Prisma.productUpdateInput = {};
if (dto.sku !== undefined) data.sku = dto.sku;
if (dto.isFeatured !== undefined) data.isFeatured = dto.isFeatured;
if (dto.typeId !== undefined) {
  data.type = dto.typeId ? { connect: { id: dto.typeId } } : { disconnect: true };
}
await this.prisma.product.update({ where: { id }, data });
```

### getAll — filter

```typescript
if (dto.isFeatured !== undefined) where.isFeatured = dto.isFeatured;
if (dto.typeId) where.typeId = dto.typeId;
if (dto.status) where.status = dto.status;
```

### getAll / getOne — return the field

Ensure `findMany`/`findFirst` selects or includes the new field (default unless `select` omits it). Add relation to `include` if needed:

```typescript
include: { type: true }
```

### search (text fields only)

```typescript
where.OR = [
  // ...existing
  { sku: { contains: search.search, mode: 'insensitive' } },
];
```

## Step 4 — Relations

| Relation | Create | Update | Read | Delete cleanup |
|----------|--------|--------|------|----------------|
| Many-to-one | `{ connect: { id } }` | connect / disconnect | `include: { rel: true }` | usually none |
| One-to-many | on parent side only | — | `include: { children: true }` | cascade or block |
| Many-to-many | junction `create`/`connect` | replace junction rows | include junction + related | `deleteMany` on junction |

**Many-to-many replace pattern:**

```typescript
await this.prisma.product_category.deleteMany({ where: { productId: id } });
await this.prisma.product_category.createMany({
  data: dto.categoryIds.map((categoryId) => ({ productId: id, categoryId })),
});
```

Add junction fields to create/update DTOs: `@RequiredArrayOfNumbersField() categoryIds: number[]`

## Step 5 — Special field types

| Type | Also update |
|------|-------------|
| File (`imageUrl`) | `@backend-nest/file-upload` — DTO file decorator, service two-phase upload |
| Bilingual (`name_en`/`name_ar`) | Both DTO fields + both in search OR |
| Sort | Usually no schema change; skip unless adding `sort` to new entity |
| Unique (slug, sku) | `@unique` in Prisma + duplicate check in create/update service |

## Step 6 — Controller

Usually **no change** — existing endpoints accept expanded DTOs.

Add controller/query changes only when:
- New filter on getAll → already covered by GetAll DTO
- New dedicated endpoint (rare) — e.g. bulk update of the new field

## Verification

```
[ ] Migration applied
[ ] Create with new field works
[ ] Update with new field works (and partial update omits undefined)
[ ] List filter works (if GetAll DTO added)
[ ] Relation connect/disconnect works
[ ] Swagger reflects new DTO fields
```

