Org Web Adapter

hungryroot/jira/be_7395_patch_api_v2_customers_self_address_creates_duplicate_address_records_on_every_call.org

ID
a8f9707f-26fc-4819-b5bd-c76f49cfaacb

BE-7395 PATCH /api/v2/customers/self/address/ creates duplicate address records on every call

- source :: https://hungryroot.atlassian.net/browse/BE-7395

- tags :: Hungryroot API Project

- epic :: EP-411 Eng Parking Lot

- sprint :: HR BE Sprint 107

- status :: Status - To Do

- priority :: Priority - Low

- points :: 1

Description

Problem

The PATCH /api/v2/customers/self/address/ endpoint creates a new Address row in

the database on every call, rather than updating the existing address in place.

The frontend calls this endpoint on every field blur (when a user clicks out of

any input field during checkout), which generates a large number of duplicate

address records.

This was observed on staging (api.staging.hungryroot.com) -- see attached

screenshot showing numerous PATCH calls in the network tab, each returning 200,

with a growing list of address records visible in the admin panel.

Root Cause Analysis

The Address model uses WormMixin (Write Once Read Many), which forces

save(force_insert=True) -- meaning every save creates a new row instead of

updating.

In app/rest/customer_address.py, the update() method explicitly documents this

behavior:

This is a custom implementation of PATCH. This will first create, then update

the customer model. This is done this way because we made address table WORM,

and this would work without breaking contract.

The WORM pattern is intentional for audit/history purposes, but the combination

with the frontend calling PATCH on every field blur creates excessive duplicate

records.

Key Code Paths

View: app/rest/customer_address.py - CustomerAddressDetail.update() (line 169)

Model: app/models/address.py - Address(WormMixin, BaseModelV1)

WORM mixin: app/lib/model_utils/worm_mixin.py - forces force_insert=True

Service: app/services/api/delivery.py - update_customer_address() also calls

Address.objects.create() (line 304)

Impact

Database bloat from duplicate address records (one per field blur event)

Unnecessary side effects on each PATCH: Braze phone sync, delivery day

recalculation, future order address assignment, and potential order reshuffles

Wasted API calls and server resources

Investigation Items

Frontend: Should the frontend debounce or batch address field changes, only

sending PATCH when the user explicitly submits (e.g., clicks "Continue to

payment")?

Backend: Should the backend deduplicate or short-circuit when the address data

hasn't meaningfully changed?

WORM pattern: Is the append-only pattern still the right tradeoff for addresses,

given the volume of records it generates? Should we consider soft-deleting old

records or adding a deduplication check before creating?

Side effects: The PATCH triggers delivery day recalculation, Braze sync, and

future order updates on every call -- these should only run when the address

actually changes.