Posted at

json-serverのDELETEで全てのアイテムや予期しないアイテムが削除される問題

json-serverは非常に便利なのですが、あるアイテムを削除しようとしたときに、なぜか全てのアイテムが削除されてしまう現象に出くわしました。


TL;DR

デフォルトで設定されている外部キーのサフィックスIdと自動的に実行されるcascading deleteの組み合わせで起きる現象なので、外部キーのサフィックスを変えてやれば(一応は)解決します。

json-server db.json --fks _id


問題の再現


db.json

{

"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のアイテムを削除します。


db.json

{

"customers": []
}

なんということでしょう、全アイテムが削除されました


原因

自動的に外部キーを設定し、自動的にcascading deleteしてしまう仕様が原因でした。


  1. デフォルトでは、Idが自動的に外部キーのサフィックスとみなされるようになっており、


  2. customerIdcustomerへの外部キーとみなされ、

  3. 任意のcustomerを削除すると、


  4. customerIdによって参照されていないcustomerはcascading deleteによって全て削除されてしまう…

ということのようです。

試しに、customerIdを外部キーと考え、以下のようにid=1id=2のアイテムがお互いを参照するようにcustomerIdを設定し、id=3を削除すると、無事にid=3のみが削除されます。


db.json

{

"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も上がっており、従属的なアイテムのみを削除+カスケードしないフラグを追加するプルリクも上がっているのですが、もう半年近く放置されています…。