ADR-0011: Admin API Dual Response Envelope¶
Status: Accepted
Date: 2026-09-18
Deciders: Staff Architect, Anil Kumar Pandey
Impacted Repos: reservation-api-server, admin-web-app
Context¶
BookingZone's admin API has evolved two distinct response envelope patterns:
- Legacy ResponseHelper envelope (
@standard_apidecorator): - Response shape:
{ "message": { "data": [...], "status": "success", ... } } - Client reads
response.message.dataand related ResponseHelper fields - Auth denials return HTTP 200 with
internal_errorin the envelope -
Used by existing admin endpoints across the codebase
-
Native Frappe REST envelope (no
@standard_api): - Response shape:
{ "data": [...], "total": N, "kpis": {...}, ... } - Client reads
response.datadirectly with documented sibling keys - Auth denials raise native
PermissionError(HTTP 403/401) - Used by new native-parity admin endpoints: calendar, outlets, CRM lists,
commerce lists, dashboard stats (in
bookingzone/api/admin_booking.pyand related modules)
The native-parity endpoints intentionally omit @standard_api so that
admin-web-app can match Frappe's native REST conventions without silent
degradation or double-wrapping. This enables:
- Consistent response shapes matching Frappe DocType API patterns
- Proper HTTP status codes for auth failures via
@require_module_permission - Sibling keys (
total,kpis,filters) at the top level - Easier migration path for screens adopting Frappe-native data fetching
Both envelopes are now part of the standing public contract. Mixing them within a single admin screen creates client-side confusion and maintenance burden.
Decision¶
The admin API supports two documented response envelopes; each screen must use exactly one.
Decision Rules¶
-
New native-parity admin endpoints MAY use
frappe.response["data"](with documented sibling keys liketotal,kpis) without the@standard_apidecorator. -
Existing ResponseHelper endpoints (
@standard_api+ResponseHelper) remain on that envelope until the consuming screen is explicitly migrated to the native envelope. -
One admin UI screen MUST consume exactly one envelope. Do not mix
message.dataand top-leveldataparsing within the same screen's data layer. -
Sister-repo PRs (especially
admin-web-app) MUST document which screens/endpoints use which envelope in the PR description or linked spec. -
Do NOT invent a third envelope. All new admin endpoints must use one of the two documented patterns.
-
Auth denials on native-envelope endpoints use
@require_module_permissionraising nativePermissionError(HTTP 403), not HTTP 200 withinternal_error.
Envelope Reference¶
| Envelope | Decorator | Response Shape | Auth Denial | Client Access |
|---|---|---|---|---|
| Legacy ResponseHelper | @standard_api |
{ message: { data, status, ... } } |
HTTP 200 + internal_error |
res.message.data |
| Native Frappe REST | None | { data, total?, kpis?, ... } |
HTTP 403 (PermissionError) | res.data |
Consequences¶
Positive¶
- Clear contract: Each endpoint has an unambiguous response shape.
- Native parity: New screens can adopt Frappe-standard patterns without wrapper overhead.
- Proper HTTP semantics: Auth failures return appropriate status codes on native endpoints.
- Incremental migration: Legacy screens continue working; new screens adopt native envelope as they're built or refactored.
- No silent degradation: Clients know which envelope to expect and parse.
Negative / Trade-offs¶
- Two patterns to document and maintain: OpenAPI extraction must eventually describe both envelopes with clear annotations.
- Developer cognitive load: Engineers must check which envelope an endpoint uses before consuming it.
- Migration burden: Existing screens stay on legacy envelope until explicitly cut over — no automatic convergence.
Risks / Mitigations¶
-
Risk: Developer accidentally mixes envelopes on one screen. Mitigation: PR review checklist; lint rule flagging mixed
message.dataandresponse.dataaccess in the same component. -
Risk: OpenAPI spec doesn't distinguish the two envelopes. Mitigation: Extend
extract-openapi.py(per ADR-0005) to annotate endpoints with their envelope type; addx-response-envelope: nativeorx-response-envelope: legacyto OpenAPI operation metadata. -
Risk: New endpoint uses neither pattern correctly. Mitigation: Code review enforcement; document examples in
bookingzone/api/README.md.
Alternatives Considered¶
-
Unify all endpoints on ResponseHelper envelope: Rejected. Requires
admin-web-appto always unwrapmessage.data, preventing Frappe-native data fetching patterns. Also loses proper HTTP status codes for auth. -
Unify all endpoints on native envelope immediately: Rejected. Would break all existing admin screens simultaneously; requires coordinated big-bang migration.
-
Create a third "v2" envelope: Rejected. Adds complexity without benefit; the two existing envelopes cover all current use cases.
-
Wrap native responses in a compatibility layer: Rejected. Adds indirection and defeats the purpose of native-parity endpoints.
Implementation Notes¶
Identifying Envelope Type¶
# Legacy ResponseHelper endpoint (uses @standard_api)
@frappe.whitelist()
@standard_api
def get_bookings():
return ResponseHelper.success(data=bookings)
# Native Frappe REST endpoint (no @standard_api)
@frappe.whitelist()
@require_module_permission("BZ Admin")
def get_calendar_events():
frappe.response["data"] = events
frappe.response["total"] = len(events)
Client-Side Pattern¶
// Legacy endpoint consumption
const legacyResponse = await api.get('/api/method/bookingzone.api.booking.get_bookings')
const bookings = legacyResponse.message.data
// Native endpoint consumption
const nativeResponse = await api.get('/api/method/bookingzone.api.admin_booking.get_calendar')
const events = nativeResponse.data
const total = nativeResponse.total
Migration Checklist for Screens¶
- Identify which endpoints the screen consumes.
- Verify all endpoints use the same envelope (or migrate them together).
- Update client data layer to parse the correct response shape.
- Update error handling for appropriate HTTP status codes.
- Document envelope choice in PR description.
References¶
- ADR-0005 — Published API contracts; OpenAPI extraction must annotate envelope types
- reservation-api-server#960 — Native→custom admin reads implementation
bookingzone/api/admin_booking.py— Native-envelope admin endpointsbookingzone/utils/response.py— ResponseHelper implementation