json-serverは非常に便利なのですが、あるアイテムを削除しようとしたときに、なぜか全てのアイテムが削除されてしまう現象に出くわしました。
TL;DR
デフォルトで設定されている外部キーのサフィックスId
と自動的に実行されるcascading deleteの組み合わせで起きる現象なので、外部キーのサフィックスを変えてやれば(一応は)解決します。
json-server db.json --fks _id
問題の再現
{
"customers": [
{
"id": 1,
"customerId": "顧客ID1",
"name": "顧客1"
},
{
"id": 2,
"customerId": "顧客ID2",
"name": "顧客2"
},
{
"id": 3,
"customerId": "顧客ID3",
"name": "顧客3"
}
]
}
何の変哲もないDBです。サロゲートキーid
を自動生成、ナチュラルキーcustomerId
を手入力することを想定した、よくある設計です。
json-server db.json
普通にjson-serverを立ち上げます。
curl -XDELETE http://localhost:3000/customers/1
普通にid=1
のアイテムを削除します。
{
"customers": []
}
なんということでしょう、全アイテムが削除されました。
原因
自動的に外部キーを設定し、自動的にcascading deleteしてしまう仕様が原因でした。
- デフォルトでは、
Id
が自動的に外部キーのサフィックスとみなされるようになっており、 -
customerId
はcustomer
への外部キーとみなされ、 - 任意の
customer
を削除すると、 -
customerId
によって参照されていないcustomer
はcascading deleteによって全て削除されてしまう…
ということのようです。
試しに、customerId
を外部キーと考え、以下のようにid=1
とid=2
のアイテムがお互いを参照するようにcustomerId
を設定し、id=3
を削除すると、無事にid=3
のみが削除されます。
{
"customers": [
{
"id": 1,
"customerId": 2,
"name": "顧客1"
},
{
"id": 2,
"customerId": 1,
"name": "顧客2"
},
{
"id": 3,
"customerId": 1,
"name": "顧客3"
}
]
}
対策
外部キーのサフィックスはオプションで設定できるので、適当な値に変えてやれば解決します。
json-server db.json --fks _id
ただし、実際の外部キーのサフィックスとしてId
を利用することはできなくなってしまうので、外部参照をせずアイテムを埋め込んでおくか、apiのpayloadの設計を変える必要があります。
しかし、モックサーバーの制限に合わせて本体の設計を変えるのは、控えめに言ってもおかしな話です。
個人的には、埋め込みによる対応でモックサーバーとしての役目は十分に果たせるので、問題ないといえばないのですが。
例外的なユースケースというわけでもないと思うので、公式に対策してほしいところです。しかし、現状ではcascading deleteはドキュメントにも記載されておらず、制御する方法もありません。
issueも上がっており、従属的なアイテムのみを削除+カスケードしないフラグを追加するプルリクも上がっているのですが、もう半年近く放置されています…。