Recommended Free Tools
iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
A default value answers one question: what should this field hold when the caller sent nothing? On create, there is no stored value to lose, so the answer is harmless. On update, the same fallback can silently overwrite data that already exists. The bug appears when an update handler cannot tell an omitted field from a field the client actually sent, so it treats every missing field as if the client had asked for the default. The fix is to establish presence first, and apply a default only where initialization needs one.
Why create and update answer different questions
Create asks what a new record should contain. Every field comes either from the request or from a default, and nothing is being replaced. Update asks what should change in a record that already holds values. For an update, an omitted field can mean keep the stored value, clear it, or reset it to a default. A create-time default quietly chooses the third meaning, which the caller usually did not intend.
How the overwrite happens
The following example is illustrative and not a measured result. Suppose a task has a priority field that defaults to "normal", and a stored task has priority set to "high". The client sends only a new title.
Free tools Windows power users keep installed
One-click scans. No signup required.
A replacement-style handler validates the body into the create model, then writes the whole object back:
#1 Best Overall
class Task(BaseModel):
title: str
priority: str = "normal"
@app.put("/tasks/{task_id}")
def replace_task(task_id: int, task: Task):
db.save(task_id, task.model_dump())
A request body of {"title": "Ship report"} stores priority: "normal". The default was applied to an omitted field, and the write replaced the stored value. This is the replacement pattern that the FastAPI tutorial “Body – Updates” describes for PUT.
A partial handler avoids this by writing only the fields the client sent:
class TaskPatch(BaseModel):
title: str | None = None
priority: str | None = None
@app.patch("/tasks/{task_id}")
def update_task(task_id: int, patch: TaskPatch):
changes = patch.model_dump(exclude_unset=True)
db.update(task_id, changes)
The same body now changes only the title, and priority stays "high". The exclude_unset=True option keeps fields that were never sent out of the write. It does not, by itself, decide what an explicit null means, which is covered below.
Create and update schemas must be allowed to differ
A single schema works for create and update only when the two operations have identical requiredness and defaults. Most APIs do not. The table shows where they usually diverge.
Rank #3
| Aspect | Create | Update (partial) |
|---|---|---|
| Required fields | Fields needed to create a valid record must be present | No field is required; the caller may omit any of them |
| Defaults | Applied to omitted fields, because nothing is stored yet | Must not be written for omitted fields unless the API documents a reset |
| Validation of supplied values | Applies to every field | Applies to each field that is present |
| Generated OpenAPI body | Describes the create input | Should be a separate schema, not a reuse of the create input |
A Rebase changelog entry illustrates the failure. It describes a case where defaultValue applied on create, while update bodies in the generated OpenAPI used the create input schema. Properties marked validation.required were therefore also marked required on the update body, so the published contract disagreed with the server’s partial-update behavior. The entry says the update schema was later derived from the input schema with the required list removed. The changelog describes these changes without confirming the release in which they shipped, so check the Rebase changelog for the version before relying on release-specific details.
Omitted versus explicit null
An omitted field and a field sent as null are different inputs, and an update contract must say what each one does. The sources reviewed take different positions.
Rank #4
| Source | Omitted field in an update | Explicit null |
|---|---|---|
| Siemens Developer Portal, “Common Operations – API Guidelines” | For PATCH, absent properties keep their current values. The guideline states: “Fields not included in the request should stay unmodified.” The server must interpret missing fields as their current values rather than null. | Not addressed in the guideline itself. It points to JSON Merge Patch as the request format, where under RFC 7396 a null value removes the member. |
| YouTube Data API, “Implementation: Partial responses” (Google for Developers) | Under this API’s update behavior, an omitted property can be deleted when it is modifiable and included in the request’s part parameter. |
Not stated in the source. This rule applies to that API only and should not be assumed elsewhere. |
| FastAPI tutorial, “Body – Updates” | In the partial example, fields never sent are excluded from the write, so stored values remain. | Determined by the handler. Because exclude_unset=True keeps explicit nulls, the handler must decide whether to clear, reject, or ignore them. |
The practical rule is to pick one meaning per field and document it. Omission should preserve, and null should either clear the field or be rejected. Whichever you choose, the rule has to be the same for every test request you run against the endpoint.
Method names do not settle the behavior
The FastAPI tutorial treats PUT as replacement and PATCH as partial update, and that is a useful convention. It is not a guarantee of how a given server behaves. The Rebase changelog describes an established PUT route whose handler merges the supplied fields and leaves the rest intact. The same entry warns that switching that route to full replacement would cause compatibility problems and data loss for clients that send partial bodies. According to the changelog, PATCH was added, PUT remained on the same partial-update handler and was deprecated in the spec, and the SDK stayed on PUT for compatibility with older servers.
Best Value
Kubernetes API concepts cover update and patch mechanisms, validation, and lost-update considerations. They do not establish a universal omission rule, so the behavior of one API should not be carried over to another.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Fixing an update handler that resets fields
- List every update route and the schema it uses. In the generated OpenAPI document, check whether the request body component is the same one used for create.
- Create an update model in which every field is optional and no default would be written for an omitted field. Track which fields were actually sent, using the framework’s set-tracking or a sentinel value.
- Write only the fields the client sent. In Pydantic v2, call
model_dump(exclude_unset=True)on the parsed patch model and apply the result to the stored record. - Decide what
nulldoes for each field: clear it, reject the request, or ignore it. In FastAPI, a rejected value returns HTTP 422 with validation details. - Decide whether a supplied array or nested object replaces the stored one or merges into it, and document the choice.
- Run three requests against a record with known values: one that omits the field, one that sends
null, and one that sends a new value. The stored result should match your documented rule each time.
Maintaining an existing endpoint
Changing an existing endpoint’s semantics can break clients that were written against the old behavior. Before changing PUT or PATCH behavior:
- Read the handler, not only the published spec. The contract and runtime behavior may disagree, as in the Rebase case.
- Identify which clients send partial bodies to PUT. Converting that route to full replacement will break them.
- Add the new behavior on a separate method or versioned route, and mark the old route as deprecated in the spec and the changelog.
- Regenerate client SDKs only after confirming which method each one calls.
Reliable public figures on how often this bug occurs are not available. Treat it as a design hazard to check for in every update route, not as a measured trend.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

