How Cascading Deletes Work with the `_dependent` Parameter in JSON Server
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 (line 151), the route handler forwards the parameter:
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 within the destroyById method (lines 202-221). This asynchronous method performs three distinct operations to maintain database consistency:
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:
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:
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:
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:
# 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
_dependentquery parameter with collection names to completely remove dependent records instead of nullifying their foreign keys. - Implementation location: The logic resides in
src/service.tswithindestroyById,nullifyForeignKey, anddeleteDependents, whilesrc/app.tshandles the HTTP parameter passing. - Parameter format: The
_dependentparameter accepts a single collection name or comma-separated values, normalized via theensureArrayhelper.
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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →