# How Cascading Deletes Work with the `_dependent` Parameter in JSON Server

> Understand cascading deletes in JSON Server using the _dependent parameter. Learn how to nullify foreign keys and purge related records for efficient data management.

- Repository: [typicode/json-server](https://github.com/typicode/json-server)
- Tags: how-to-guide
- Published: 2026-03-01

---

**JSON Server's `_dependent` query parameter lets you delete a resource and automatically cascade the deletion to related collections by nullifying foreign keys and optionally purging dependent records.**

The `typicode/json-server` library implements cascading deletes through a dedicated service layer that handles referential integrity. When you delete a parent resource, the system can either nullify foreign key references in related collections or completely remove dependent records, depending on how you use the `_dependent` parameter.

## HTTP Entry Point and Parameter Handling

The cascading delete process begins in the HTTP routing layer. When a client sends a `DELETE /:name/:id` request, the router extracts the `_dependent` query parameter and passes it to the service layer.

In [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) (line 151), the route handler forwards the parameter:

```typescript
res.locals['data'] = await service.destroyById(name, id, req.query['_dependent'])

```

If `_dependent` is omitted, the value is `undefined`. When provided, it can be a single string or an array of resource names (e.g., `comments,likes`).

## The `destroyById` Service Method

The core logic resides in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts) within the `destroyById` method (lines 202-221). This asynchronous method performs three distinct operations to maintain database consistency:

```typescript
async destroyById(
  name: string,
  id: string,
  dependent?: string | string[],
): Promise<Item | undefined> {
  // ... find and splice the item ...
  nullifyForeignKey(this.#db, name, id)               // step 2
  const dependents = ensureArray(dependent)           // normalise input
  deleteDependents(this.#db, name, dependents)        // step 3
  await this.#db.write()
  return item
}

```

### Step 1: Removing the Primary Item

First, the method locates the target item in its collection and removes it from the in-memory database array.

### Step 2: Nullifying Foreign Key References

The `nullifyForeignKey` function (lines 49-64) iterates over **all** collections except the one being deleted. It identifies foreign key fields using the pattern `${singular(name)}Id` and sets matching values to `null`:

```typescript
function nullifyForeignKey(db: Low<Data>, name: string, id: string) {
  const foreignKey = `${inflection.singularize(name)}Id`
  Object.entries(db.data).forEach(([key, items]) => {
    if (key === name) return
    if (Array.isArray(items)) {
      items.forEach(item => {
        if (item[foreignKey] === id) item[foreignKey] = null
      })
    }
  })
}

```

This prevents dangling references by breaking the relationship between the deleted parent and its former children.

### Step 3: Deleting Dependent Collections

When the `_dependent` parameter is provided, the `deleteDependents` function (lines 67-78) removes entire rows from the specified collections. It filters out items where the foreign key is `null`, effectively keeping only orphans that belonged to the deleted parent:

```typescript
function deleteDependents(db: Low<Data>, name: string, dependents: string[]) {
  const foreignKey = `${inflection.singularize(name)}Id`
  Object.entries(db.data).forEach(([key, items]) => {
    if (key === name || !dependents.includes(key)) return
    if (Array.isArray(items)) {
      db.data[key] = items.filter(item => item[foreignKey] !== null)
    }
  })
}

```

## Parameter Normalization

To handle both single values and comma-separated strings consistently, the `ensureArray` helper (lines 19-21) normalizes the input:

```typescript
function ensureArray(arg: string | string[] = []): string[] {
  return Array.isArray(arg) ? arg : [arg]
}

```

This allows the API to accept `_dependent=comments` or `_dependent=comments,likes` interchangeably.

## Practical Examples

The following `curl` commands demonstrate the cascading delete behavior against a typical JSON Server instance:

```bash

# Delete a post and only nullify the postId in comments (default behavior)

curl -X DELETE http://localhost:3000/posts/1

# Delete a post and also delete all its comments

curl -X DELETE "http://localhost:3000/posts/1?_dependent=comments"

# Delete a post and cascade to multiple dependent collections

curl -X DELETE "http://localhost:3000/posts/1?_dependent=comments,likes"

```

## Summary

- **Default behavior**: Deleting a resource automatically nullifies foreign key references (`${resourceName}Id`) in all other collections to prevent dangling pointers.
- **Cascading deletes**: Append the `_dependent` query parameter with collection names to completely remove dependent records instead of nullifying their foreign keys.
- **Implementation location**: The logic resides in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts) within `destroyById`, `nullifyForeignKey`, and `deleteDependents`, while [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) handles the HTTP parameter passing.
- **Parameter format**: The `_dependent` parameter accepts a single collection name or comma-separated values, normalized via the `ensureArray` helper.

## Frequently Asked Questions

### What happens if I omit the `_dependent` parameter when deleting a resource?

If you omit `_dependent`, JSON Server deletes the primary item and runs `nullifyForeignKey` on all other collections. This sets any matching foreign key fields (e.g., `postId`) to `null` but preserves the dependent records themselves.

### Can I cascade deletes to multiple collections at once?

Yes. You can pass multiple collection names as a comma-separated string (e.g., `_dependent=comments,likes`) or as repeated query parameters depending on your HTTP client. The `ensureArray` function in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts) normalizes both formats into an array for processing.

### How does JSON Server identify which records to delete or nullify?

The system uses a naming convention based on the singular form of the parent resource. For a `posts` collection, it looks for a `postId` field in other collections. Records in the `_dependent` list are deleted only if their foreign key is `null` after the parent deletion, ensuring only orphans are removed.

### Is the cascading delete behavior atomic?

The operation is atomic within the context of the in-memory database update. The `destroyById` method performs all deletions and nullifications before calling `this.#db.write()`, which persists the entire state to the JSON file in a single write operation. If the process crashes before `write()` completes, the changes are lost, but the database remains consistent.