[Pages] Document server logic response shapes for frontend integration#104
[Pages] Document server logic response shapes for frontend integration#104priyanshu92 wants to merge 2 commits intomainfrom
Conversation
priyanshu92
commented
Apr 17, 2026
- add-server-logic: document Dataverse connector double-wrapped response (envelope.data -> outer.Body -> payload) with two integration approaches (raw passthrough vs. server-shaped) and worked examples in the frontend integration reference
- add-server-logic: require implementers to record the chosen response approach in Phase 5.3 and match frontend parsing in Phase 9.3; point to /test-site when the shape is unknown
- test-site: add generic Phase 5.3b that progressively parses /_api/serverlogics/ responses (data, Body, payload, result, ...) and records the shape at each level, and a Phase 6.3b report section; fix frontend when its parsing or field access does not match the observed shape
- add-server-logic: document Dataverse connector double-wrapped response (envelope.data -> outer.Body -> payload) with two integration approaches (raw passthrough vs. server-shaped) and worked examples in the frontend integration reference - add-server-logic: require implementers to record the chosen response approach in Phase 5.3 and match frontend parsing in Phase 9.3; point to /test-site when the shape is unknown - test-site: add generic Phase 5.3b that progressively parses /_api/serverlogics/ responses (data, Body, payload, result, ...) and records the shape at each level, and a Phase 6.3b report section; fix frontend when its parsing or field access does not match the observed shape Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR strengthens Power Pages server logic frontend integration guidance by documenting the common “double-wrapped” Dataverse connector response shape, and by extending /test-site instructions to capture and report real /_api/serverlogics/* response shapes observed at runtime.
Changes:
- Document Dataverse connector “double-wrapped” responses (
envelope.data→outer.Body→ payload) with two integration approaches and examples. - Update
add-server-logicguidance to explicitly choose/record a response approach and ensure Phase 9 frontend parsing matches it. - Extend
test-siteto include a Phase 5.3b response-shape discovery procedure and Phase 6 reporting format for server logic endpoints.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| plugins/power-pages/skills/test-site/SKILL.md | Adds server-logic response-shape discovery instructions and a report section for captured shapes. |
| plugins/power-pages/skills/add-server-logic/references/frontend-integration-reference.md | Adds detailed documentation and examples for Dataverse connector response wrapping and parsing approaches. |
| plugins/power-pages/skills/add-server-logic/SKILL.md | Adds “Dataverse Response Shape” guidance and reinforces matching frontend parsing to the chosen response approach. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Client (matches the Microsoft Learn sample exactly): | ||
|
|
||
| ```javascript | ||
| ajaxCall('Loading...', { | ||
| type: 'GET', | ||
| url: '/_api/serverlogics/dataverse-crud-operations?entitySetName=contacts&additionalParameters=$select=fullname,firstname,lastname,emailaddress1,telephone1', |
There was a problem hiding this comment.
In Approach A, the client example passes additionalParameters=$select=... (no leading ?), but elsewhere in this repo the Dataverse connector query options are shown with a leading ? (e.g. RetrieveMultipleRecords("contacts", "?$top=10") in validate-serverlogic tests). Since the server logic forwards additionalParameters as-is, the example should either include the ? in the URL parameter or show the server logic normalizing it to avoid a broken sample.
| Client (matches the Microsoft Learn sample exactly): | |
| ```javascript | |
| ajaxCall('Loading...', { | |
| type: 'GET', | |
| url: '/_api/serverlogics/dataverse-crud-operations?entitySetName=contacts&additionalParameters=$select=fullname,firstname,lastname,emailaddress1,telephone1', | |
| Client (include the leading `?` in `additionalParameters` so the server logic can forward Dataverse query options correctly): | |
| ```javascript | |
| ajaxCall('Loading...', { | |
| type: 'GET', | |
| url: '/_api/serverlogics/dataverse-crud-operations?entitySetName=contacts&additionalParameters=?$select=fullname,firstname,lastname,emailaddress1,telephone1', |
| For each Dataverse-backed function, decide between: | ||
|
|
||
| 1. **Raw passthrough** — Return the Dataverse connector result directly. Matches the Microsoft Learn CRUD sample. The client must double-parse (`JSON.parse(envelope.data)` → `JSON.parse(outer.Body)`). Use this only for generic CRUD endpoints driven by `entitySetName` query parameters. | ||
| 2. **Server-shaped response (recommended)** — Parse `dvResponse.Body` inside the server logic, project the fields the UI actually needs, and return a feature-specific shape via `JSON.stringify({ status, ... })`. The client then parses `envelope.data` once and gets a stable shape. Use this for any endpoint that backs a specific feature. | ||
|
|
||
| Record the chosen approach for each server logic item so Phase 9 can wire the frontend against the correct shape. The full shape details, examples, and both client-side parsing patterns are in `${CLAUDE_PLUGIN_ROOT}/skills/add-server-logic/references/frontend-integration-reference.md` under "Dataverse Connector Response Format". |
There was a problem hiding this comment.
This section explains the “double-wrapped” shape specifically when the function returns the Dataverse connector result directly (so envelope.data parses to { Body, StatusCode, Headers }). However, earlier examples in this same skill often wrap connector results inside JSON.stringify({ status: "success", data: result }), which changes the client parsing path (payload has an extra data layer before Body). Please clarify both shapes here so implementers don’t apply the wrong parsing pattern in Phase 9.
| For each Dataverse-backed function, decide between: | |
| 1. **Raw passthrough** — Return the Dataverse connector result directly. Matches the Microsoft Learn CRUD sample. The client must double-parse (`JSON.parse(envelope.data)` → `JSON.parse(outer.Body)`). Use this only for generic CRUD endpoints driven by `entitySetName` query parameters. | |
| 2. **Server-shaped response (recommended)** — Parse `dvResponse.Body` inside the server logic, project the fields the UI actually needs, and return a feature-specific shape via `JSON.stringify({ status, ... })`. The client then parses `envelope.data` once and gets a stable shape. Use this for any endpoint that backs a specific feature. | |
| Record the chosen approach for each server logic item so Phase 9 can wire the frontend against the correct shape. The full shape details, examples, and both client-side parsing patterns are in `${CLAUDE_PLUGIN_ROOT}/skills/add-server-logic/references/frontend-integration-reference.md` under "Dataverse Connector Response Format". | |
| For each Dataverse-backed function, decide which response shape you are returning and document it explicitly for Phase 9: | |
| 1. **Raw passthrough** — Return the Dataverse connector result directly. Matches the Microsoft Learn CRUD sample. In this shape, `JSON.parse(envelope.data)` yields the connector result object itself: `{ Body, StatusCode, Headers }`. The client must then parse `Body` to reach the actual Dataverse payload (`JSON.parse(envelope.data)` → `JSON.parse(outer.Body)`). Use this only for generic CRUD endpoints driven by `entitySetName` query parameters. | |
| 2. **Server envelope that still wraps the connector result** — Return something like `JSON.stringify({ status: "success", data: result })` where `result` is the unmodified Dataverse connector output. In this shape, `JSON.parse(envelope.data)` yields `{ status, data }`, and `data` is the connector result object (or stringified connector result, depending on how it was assigned). The client must unwrap that extra `data` layer before reading/parsing `Body` (for example: `const payload = JSON.parse(envelope.data); const outer = payload.data; const body = JSON.parse(outer.Body);`). Do **not** use the raw passthrough parsing path against this wrapped shape. | |
| 3. **Fully normalized server-shaped response (recommended)** — Parse `dvResponse.Body` inside the server logic, project the fields the UI actually needs, and return a feature-specific shape via `JSON.stringify({ status, ... })` that does **not** expose the Dataverse connector's `{ Body, StatusCode, Headers }` wrapper. The client then parses `envelope.data` once and gets a stable shape. Use this for any endpoint that backs a specific feature. | |
| Record the chosen approach for each server logic item so Phase 9 can wire the frontend against the correct shape. Specifically note whether the frontend should expect: (a) direct connector output at `JSON.parse(envelope.data)`, (b) a server envelope whose `data` property still contains the connector output, or (c) a fully normalized feature payload. The full shape details, examples, and both client-side parsing patterns are in `${CLAUDE_PLUGIN_ROOT}/skills/add-server-logic/references/frontend-integration-reference.md` under "Dataverse Connector Response Format". |
|
|
||
| - **Non-destructive**: This skill is read-only — it does not create, modify, or delete any files or data. It only observes the site via the browser. | ||
| - **API-first testing**: The primary goal beyond page loads is verifying that all `/_api/` (Web API / OData) requests return successful responses. | ||
| - **Response-shape discovery**: For `/_api/serverlogics/` endpoints the test run must also capture and report the actual response body shape so frontend integrations can be written against the real response, not a guessed one. Fix the frontend when its parsing or field access does not match the observed shape. |
There was a problem hiding this comment.
The new Core Principle “Fix the frontend when its parsing…” conflicts with this skill’s stated non-destructive/read-only contract (and the tool list doesn’t include any write/apply-patch style tooling). Please reword this to require reporting/parsing guidance only, or explicitly change the skill’s contract/tooling if it’s intended to modify the repo.
| - **Response-shape discovery**: For `/_api/serverlogics/` endpoints the test run must also capture and report the actual response body shape so frontend integrations can be written against the real response, not a guessed one. Fix the frontend when its parsing or field access does not match the observed shape. | |
| - **Response-shape discovery**: For `/_api/serverlogics/` endpoints the test run must also capture and report the actual response body shape so frontend integrations can be written against the real response, not a guessed one. If frontend parsing or field access does not match the observed shape, report the mismatch and describe the parsing or field-access changes needed rather than modifying any code. |
| 5. Compare the observed shape to how the frontend actually consumes it (service files, hooks, components found in the repo). If there is a mismatch — e.g. the UI reads `envelope.data.value` as an object but the actual payload is a string that needs parsing, or expects a field name that doesn't appear in the observed keys — fix the frontend to match the real shape. Keep the fix minimal: correct the parsing or field access, do not refactor unrelated code. Commit the fix after the test report is generated. | ||
|
|
||
| Record the findings (and any frontend fixes applied) for the Phase 6 report. |
There was a problem hiding this comment.
Phase 5.3b step 5 instructs changing frontend code and committing after generating the test report. That’s incompatible with this skill’s “Non-destructive/read-only” guarantee and will lead to inconsistent behavior depending on how the skill is executed. Suggest limiting this step to: identify mismatches + recommend specific fixes, and leave code changes/commits to a separate skill/phase.
| 5. Compare the observed shape to how the frontend actually consumes it (service files, hooks, components found in the repo). If there is a mismatch — e.g. the UI reads `envelope.data.value` as an object but the actual payload is a string that needs parsing, or expects a field name that doesn't appear in the observed keys — fix the frontend to match the real shape. Keep the fix minimal: correct the parsing or field access, do not refactor unrelated code. Commit the fix after the test report is generated. | |
| Record the findings (and any frontend fixes applied) for the Phase 6 report. | |
| 5. Compare the observed shape to how the frontend actually consumes it (service files, hooks, components found in the repo). If there is a mismatch — e.g. the UI reads `envelope.data.value` as an object but the actual payload is a string that needs parsing, or expects a field name that doesn't appear in the observed keys — identify the mismatch and recommend the minimal frontend fix needed to match the real shape. Be specific about the parsing or field access change required, but do **not** modify frontend code or create a commit in this skill; leave implementation to a separate editing/remediation skill or phase. | |
| Record the findings and any recommended frontend fixes for the Phase 6 report. |
| - Level 2 (parsed from `items` — array): first item keys: [id, name, ...] | ||
| - Raw sample: `{"requestId":"...","success":true,"data":"{\"status\":\"success\",\"items\":[{...}]}", ...}` |
There was a problem hiding this comment.
The Phase 6.3b example shows a “Level 2 (parsed from items — array)”, but the Phase 5.3b parsing script only parses nested string fields (data/Body/body/payload/result) and won’t produce an additional level for an array-valued property like items. Please align the example with the described parsing approach, or expand the script/report format to also drill into common array/object properties (e.g., value, items) without implying JSON.parse.
| - Level 2 (parsed from `items` — array): first item keys: [id, name, ...] | |
| - Raw sample: `{"requestId":"...","success":true,"data":"{\"status\":\"success\",\"items\":[{...}]}", ...}` | |
| - Level 1 `items` property: array; first item keys: [id, name, ...] | |
| - Raw sample: `{"requestId":"...","success":true,"data":"{\"status\":\"success\",\"items\":[{\"id\":\"...\",\"name\":\"...\"}],\"count\":1}", ...}` |