Skip to content

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:

  1. Legacy ResponseHelper envelope (@standard_api decorator):
  2. Response shape: { "message": { "data": [...], "status": "success", ... } }
  3. Client reads response.message.data and related ResponseHelper fields
  4. Auth denials return HTTP 200 with internal_error in the envelope
  5. Used by existing admin endpoints across the codebase

  6. Native Frappe REST envelope (no @standard_api):

  7. Response shape: { "data": [...], "total": N, "kpis": {...}, ... }
  8. Client reads response.data directly with documented sibling keys
  9. Auth denials raise native PermissionError (HTTP 403/401)
  10. Used by new native-parity admin endpoints: calendar, outlets, CRM lists, commerce lists, dashboard stats (in bookingzone/api/admin_booking.py and 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

  1. New native-parity admin endpoints MAY use frappe.response["data"] (with documented sibling keys like total, kpis) without the @standard_api decorator.

  2. Existing ResponseHelper endpoints (@standard_api + ResponseHelper) remain on that envelope until the consuming screen is explicitly migrated to the native envelope.

  3. One admin UI screen MUST consume exactly one envelope. Do not mix message.data and top-level data parsing within the same screen's data layer.

  4. Sister-repo PRs (especially admin-web-app) MUST document which screens/endpoints use which envelope in the PR description or linked spec.

  5. Do NOT invent a third envelope. All new admin endpoints must use one of the two documented patterns.

  6. Auth denials on native-envelope endpoints use @require_module_permission raising native PermissionError (HTTP 403), not HTTP 200 with internal_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.data and response.data access 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; add x-response-envelope: native or x-response-envelope: legacy to 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-app to always unwrap message.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

  1. Identify which endpoints the screen consumes.
  2. Verify all endpoints use the same envelope (or migrate them together).
  3. Update client data layer to parse the correct response shape.
  4. Update error handling for appropriate HTTP status codes.
  5. 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 endpoints
  • bookingzone/utils/response.py — ResponseHelper implementation