| name | applying-backend-diff |
| description | Use when the user shares a backend PR, patch URL, or diff and asks to mirror, port, or add those API endpoint changes into the @connectedxm/admin-sdk TypeScript SDK (queries/mutations files under src/queries and src/mutations). Triggered by phrases like "update admin sdk with this diff", "mirror this endpoint", "add this to admin-sdk", "port these endpoints", or sharing a backend PR while in the admin-sdk repo or one of its worktrees. |
Applying a Backend Diff to admin-sdk
Overview
Backend adds or changes an admin API endpoint → admin-sdk needs a matching query or mutation file. The repo enforces a strict file shape because scripts/generate.ts walks the AST to emit openapi.json, and deviations are silently dropped from the generated OpenAPI spec — and therefore from the downstream @connectedxm/admin-sdk typescript-axios package.
The repo's CLAUDE.md is the source of truth for conventions; this skill is the procedural recipe for going from a backend diff to a working, lint-clean SDK change.
When to use
- User shares a backend PR URL (github.com, patch-diff.githubusercontent.com) and asks to update the SDK.
- User pastes a diff and asks to mirror endpoints in admin-sdk.
- User asks to "add this endpoint" to admin-sdk and provides backend code.
Don't use for:
- Hand-editing the generated
sdks/typescript/ package — it's regenerated from openapi.json.
- Changes that don't involve adding/modifying API endpoints (e.g., dependency bumps, internal refactors).
Workflow
1. Get the diff
If the user gave a GitHub PR URL or a patch-diff.githubusercontent.com URL, fetch via gh CLI rather than WebFetch — patch-diff URLs redirect through auth and frequently 401:
gh pr view <num> --repo <owner>/<repo> --json title,body,files,additions,deletions
gh api repos/<owner>/<repo>/pulls/<num>/files --jq '.[] | {path: .filename, patch: .patch}'
The PR description often names the pattern to mirror ("mirrors the existing X(...)") — read it before scanning code.
2. Extract, per endpoint
- HTTP method + URL path (e.g.
GET /payments/:paymentId/tax-metadata)
- Path / query params and their types
- Request body shape (mutations only)
- Response
data shape — including all branches (soft-skip messages, error messages, success payload)
3. Find the canonical example to mirror
Locate the parent resource directory by grepping for an existing endpoint on the same resource:
grep -rln "/payments/" src/queries/ src/mutations/
Then mirror the closest-matching file:
| Endpoint type | Canonical example | Wrapper helper |
|---|
| Single GET | src/queries/<resource>/useGetX.ts | useConnectedSingleQuery |
| Paginated list | src/queries/<resource>/useGetXs.ts | useConnectedInfiniteQuery |
| Cursor list | (find an existing one in the repo) | useConnectedCursorQuery |
| Mutation | src/mutations/<resource>/useUpdateX.ts / useCreateX.ts / useDeleteX.ts | useConnectedMutation |
4. Reuse existing types
Before adding to src/interfaces.ts or src/params.ts, grep both files:
grep -n "^export.*<TypeName>\b" src/interfaces.ts src/params.ts
For genuinely dynamic response blobs (e.g., raw third-party transaction data, free-form metadata), Record<string, any> is supported by the generator — see scripts/generate.ts:570, which maps it to an OpenAPI object with additionalProperties. Don't invent a misleading concrete interface just to "stay consistent."
5. Create the file with exact shape
The AST parser at scripts/generate.ts looks for one exported arrow function whose first parameter type extends SingleQueryParams / InfiniteQueryParams / CursorQueryParams / MutationParams and whose return type is Promise<ConnectedXMResponse<T>>. Get this wrong and the endpoint silently drops from openapi.json.
Query file exports, in order:
*_QUERY_KEY — array key, nested under parent resource's key (e.g. [...PAYMENT_QUERY_KEY(id), "TAX_METADATA"])
SET_*_QUERY_DATA — cache setter
- Params interface extending
SingleQueryParams / InfiniteQueryParams / CursorQueryParams
async API function returning Promise<ConnectedXMResponse<T>>
use* hook calling the appropriate useConnected*Query
Mutation file exports, in order:
- Params interface extending
MutationParams
async API function (call queryClient.invalidateQueries(...) / setters after the request succeeds so consumers see fresh data)
use* hook calling useConnectedMutation
Path aliases @src/* and @interfaces are configured — use them.
6. Regenerate barrels
npm run exports
Never hand-edit index.ts files — .cursor/rules/index-exports.mdc forbids it, and the script will overwrite manual edits.
7. Verify
npx tsc
npm run lint
Both must pass clean before reporting completion.
8. Do NOT run npm run generate
CI regenerates openapi.json on the staging → main release PR per .github/workflows/. Running it locally produces noisy unrelated diffs from other in-flight endpoints. Mention to the user that CI handles it.
Common mistakes
| Mistake | Fix |
|---|
| File deviates from canonical shape → generator silently drops it | Diff your new file line-by-line against the closest existing file in the same directory |
Adding a new type that already exists in interfaces.ts / params.ts | Grep both files first; reuse types verbatim |
Hand-editing index.ts barrels | Run npm run exports |
| Inventing a misleading concrete interface for a dynamic provider blob | Use Record<string, any> — the generator handles it |
Running npm run generate locally | Don't; CI does it on the release PR |
Using WebFetch on github.com/.../pull/N.diff | Use gh pr view and gh api repos/.../pulls/N/files instead |
cd-ing out of the current worktree | Stay in the worktree directory; use absolute paths |
Red flags — stop and reconsider
- About to write a new interface without grepping for it first
- About to edit
src/<dir>/index.ts by hand
- File shape doesn't match the canonical example (different export order, missing
SET_*_QUERY_DATA, return type not Promise<ConnectedXMResponse<T>>)
- Skipping
npx tsc or npm run lint because "the change is small"
- Running
npm run generate to "make sure it works" — don't