My idea was to use a null field on the PATCH body as a signal to remove this field of the object. Is that a good approach?
No.
The right way to do it is to use a general purpose media type that describes the edits that you have made to the local representation of the resource, and implementing the appropriate interpreter on the server.
Here's an example using JSON Patch.
PATCH /my/data HTTP/1.1
Content-Type: application/json-patch+json
[
{ "op": "remove", "path": "/a/b/c" }
]
Here's another example of the same edit, using JSON Merge Patch
PATCH /my/data HTTP/1.1
Content-Type: application/merge-patch+json
{
"a": {
"b": {
"c": null
}
}
}
The important idea here being that we are using the Content-Type header to describe how the patch document is to be interpreted.
Servers are expected to use an Accept-Patch header to signal the supported patch document formats.
I would like to avoid the need to PUT the entire new object.
Unless the original representation is large compared to your HTTP headers, and the representation of the patch document is much smaller, the benefits of PUT will usually outweigh the costs.
Also, there's nothing wrong with supporting both, and letting the client decide for itself which method semantics are most appropriate.