API Proxy Update to Operations 2.21
Analysis of the deployed Operations OpenAPI spec at
https://dev.alpha002.io.arda.cards/v1/item/docs/openApi.json compared against
the current @arda-cards/api-proxy ItemProxy implementation. Identifies gaps
and proposes changes.
Source
Section titled “Source”- OpenAPI spec: Operations 2.21, deployed to dev environment
- api-proxy:
src/reference/item/proxy.ts(currentmainbranch)
1. New Endpoint: Image Upload
Section titled “1. New Endpoint: Image Upload”The backend has deployed a new image upload credential endpoint.
Deployed Path
Section titled “Deployed Path”POST /v1/item/image-uploadThis is a module-scoped endpoint (under /v1/item/), not entity-scoped.
There is no itemEId path parameter. This differs from the system design
specification which assumed POST /v1/item/item/<itemEId>/image-upload-url
(TD-13). The goal.md and BFF specification documents should be updated to
reflect the actual deployed path.
Request Schema (ImageUploadRequest)
Section titled “Request Schema (ImageUploadRequest)”{ contentType: string; // required — e.g. "image/jpeg" contentLength: number; // required — int64, file size in bytes}Response Schema (ImageUploadResponse)
Section titled “Response Schema (ImageUploadResponse)”{ uploadUrl: string; // required — presigned S3 POST URL formFields: Record<string, string>; // required — form fields for multipart POST objectKey: string; // required — S3 object key cdnUrl: string; // required — CDN URL for the uploaded image}Required Headers
Section titled “Required Headers”| Header | Required | Description |
|---|---|---|
X-Author | Yes | Author of the call |
X-Tenant-Id | Yes | Tenant identifier |
X-Request-ID | No | Server generates one if not provided |
Error Responses
Section titled “Error Responses”Standard ErrorResponse for all non-200 status codes (400, 401, 403, 413, etc.).
2. Coverage Gap Analysis
Section titled “2. Coverage Gap Analysis”Currently Covered (no changes needed)
Section titled “Currently Covered (no changes needed)”| Capability | Endpoint | Proxy Method |
|---|---|---|
| Create item | POST /v1/item/item | create() |
| Get item | GET /v1/item/item/{id} | get() |
| Get by record ID | GET /v1/item/item/rid/{rid} | getByRecordId() |
| Update item | PUT /v1/item/item/{id} | update() |
| Delete item | DELETE /v1/item/item/{id} | delete() |
| Query items | POST /v1/item/item/query | query() |
| Query history | POST /v1/item/item/{id}/history | queryHistory() |
| Get draft | GET /v1/item/item/{id}/draft | getDraft() |
| Update draft | PUT /v1/item/item/{id}/draft | updateDraft() |
| List supplies | GET /v1/item/item/{id}/supply | getSupplies() |
| Create supply | POST /v1/item/item/{id}/supply | createSupply() |
| Update supply | PUT /v1/item/item/{id}/supply/{sid} | updateSupply() |
| Delete supply | DELETE /v1/item/item/{id}/supply/{sid} | deleteSupply() |
| Lookup suppliers | GET /v1/item/lookup-suppliers | lookupSuppliers() |
| Lookup units | GET /v1/item/lookup-units | lookupUnits() |
| Print labels | POST /v1/item/item/print-label | printLabels() |
| Print breadcrumbs | POST /v1/item/item/print-breadcrumb | printBreadcrumbs() |
| CSV upload URL | POST /v1/item/upload-job/upload-url | createUploadUrl() |
| CSV upload status | GET /v1/item/upload-job/{jobId} | getUploadJobStatus() |
Missing — Image Upload (new in Operations 2.21)
Section titled “Missing — Image Upload (new in Operations 2.21)”| Endpoint | Method | Purpose | Priority |
|---|---|---|---|
/v1/item/image-upload | POST | Presigned S3 POST credentials + CDN URL | Must-do |
Missing — Draft Delete
Section titled “Missing — Draft Delete”| Endpoint | Method | Purpose | Priority |
|---|---|---|---|
/v1/item/item/{id}/draft | DELETE | Discard draft | Should-do |
The api-proxy has getDraft() and updateDraft() but is missing deleteDraft().
Missing — Lookup Endpoints (10 methods)
Section titled “Missing — Lookup Endpoints (10 methods)”All follow the same pattern as lookupSuppliers/lookupUnits: GET with a
name query parameter, returning string[].
| Endpoint | Method | Purpose | Priority |
|---|---|---|---|
/v1/item/lookup-departments | GET | Fuzzy match department names | Should-do |
/v1/item/lookup-facilities | GET | Fuzzy match facility names | Should-do |
/v1/item/lookup-items | GET | Fuzzy match item names | Should-do |
/v1/item/lookup-locations | GET | Fuzzy match location names | Should-do |
/v1/item/lookup-sublocations | GET | Fuzzy match sublocation names | Should-do |
/v1/item/lookup-subtypes | GET | Fuzzy match subtype names | Should-do |
/v1/item/lookup-types | GET | Fuzzy match type names | Should-do |
/v1/item/lookup-usecases | GET | Fuzzy match use case names | Should-do |
Note: lookup-departments and lookup-facilities are new lookups not
previously available. The others (lookup-items, lookup-locations, etc.) may
have been available before but were never added to the api-proxy.
Missing — Bulk and Cursor Pagination
Section titled “Missing — Bulk and Cursor Pagination”| Endpoint | Method | Purpose | Priority |
|---|---|---|---|
/v1/item/item/bulk | POST | Bulk create items | Must-do |
/v1/item/item/bulk | PUT | Bulk update items | Must-do |
/v1/item/item/query/{page} | GET | Cursor-based paginated query | Must-do |
/v1/item/item/{id}/history/{page} | GET | Cursor-based paginated history | Must-do |
No current consumer in arda-frontend-app, but included for full API coverage.
ItemInput Schema
Section titled “ItemInput Schema”The imageUrl field is already string | null in both the deployed OpenAPI
spec and the existing api-proxy ItemInput type. No type changes needed for
image URL persistence.
3. Proposed Changes
Section titled “3. Proposed Changes”Must-Do: Image Upload Proxy Method
Section titled “Must-Do: Image Upload Proxy Method”Types (src/reference/item/types.ts):
/** Request for presigned S3 POST credentials to upload an item image. */export interface ImageUploadRequest { contentType: string; contentLength: number;}
/** Presigned S3 POST credentials and CDN URL for an uploaded image. */export interface ImageUploadResponse { uploadUrl: string; formFields: Record<string, string>; objectKey: string; cdnUrl: string;}Proxy method (src/reference/item/proxy.ts):
// -------------------------------------------------------------------------// Image upload operations// -------------------------------------------------------------------------
createImageUploadUrl(request: ImageUploadRequest): Promise<ImageUploadResponse> { return this.client.request("POST", this.client.buildUrl("/image-upload"), request);}Tests (tests/reference/item/proxy.test.ts):
- Happy path: correct URL construction, request body passed, response parsed
- Verify path is
/v1/item/image-upload(module-scoped, not entity-scoped) - Error propagation (non-200 status)
Should-Do: Draft Delete
Section titled “Should-Do: Draft Delete”Proxy method (src/reference/item/proxy.ts):
deleteDraft(entityId: string): Promise<ItemRecord> { return this.client.request("DELETE", this.client.buildUrl(`/item/${entityId}/draft`));}Tests: Happy path, error propagation.
Should-Do: Missing Lookup Methods
Section titled “Should-Do: Missing Lookup Methods”All follow the existing lookupSuppliers pattern:
lookupDepartments(query: string): Promise<string[]> { return this.client.lookup<string[]>("lookup-departments", { name: query });}
lookupFacilities(query: string): Promise<string[]> { return this.client.lookup<string[]>("lookup-facilities", { name: query });}
lookupItems(query: string): Promise<string[]> { return this.client.lookup<string[]>("lookup-items", { name: query });}
lookupLocations(query: string): Promise<string[]> { return this.client.lookup<string[]>("lookup-locations", { name: query });}
lookupSublocations(query: string): Promise<string[]> { return this.client.lookup<string[]>("lookup-sublocations", { name: query });}
lookupSubtypes(query: string): Promise<string[]> { return this.client.lookup<string[]>("lookup-subtypes", { name: query });}
lookupTypes(query: string): Promise<string[]> { return this.client.lookup<string[]>("lookup-types", { name: query });}
lookupUsecases(query: string): Promise<string[]> { return this.client.lookup<string[]>("lookup-usecases", { name: query });}Tests: One test per method following the existing lookup test pattern.
Must-Do: Bulk Operations
Section titled “Must-Do: Bulk Operations”Types (src/reference/item/types.ts):
/** Entry in a bulk update request. */export interface BulkUpdateEntry { eId: string; payload: ItemInput;}
/** Request body for bulk update operations. */export interface BulkUpdateRequest { updates: BulkUpdateEntry[];}Proxy methods (src/reference/item/proxy.ts):
bulkCreate(inputs: ItemInput[]): Promise<ItemRecord[]> { return this.client.request("POST", this.client.buildUrl("/item/bulk"), inputs);}
bulkUpdate(request: BulkUpdateRequest): Promise<ItemRecord[]> { return this.client.request("PUT", this.client.buildUrl("/item/bulk"), request);}Tests: Verify URL /v1/item/item/bulk, correct HTTP method, request body,
array response.
Must-Do: Cursor-Based Pagination
Section titled “Must-Do: Cursor-Based Pagination”Proxy methods (src/reference/item/proxy.ts):
getQueryPage(pageId: string): Promise<ItemPage> { return this.client.request("GET", this.client.buildUrl(`/item/query/${pageId}`));}
getHistoryPage(entityId: string, pageId: string): Promise<ItemPage> { return this.client.request("GET", this.client.buildUrl(`/item/${entityId}/history/${pageId}`));}Tests: Verify URL construction with page ID tokens, GET method, PageResult response.
Barrel Export Updates
Section titled “Barrel Export Updates”Add new types to src/reference/item/index.ts and src/reference/index.ts
barrel exports:
export type { BulkUpdateEntry, BulkUpdateRequest, ImageUploadRequest, ImageUploadResponse,} from "./types.js";4. Implementation Status
Section titled “4. Implementation Status”All changes described above have been implemented and verified:
- Typecheck: passes
- Lint: passes
- Tests: 225 total (39 for ItemProxy — 14 new tests added)
- Build: passes
New methods added to ItemProxy:
createImageUploadUrl(request)— image upload credentialsdeleteDraft(entityId)— discard draftlookupDepartments(query)— 8 new lookup methodslookupFacilities(query)lookupItems(query)lookupLocations(query)lookupSublocations(query)lookupSubtypes(query)lookupTypes(query)lookupUsecases(query)bulkCreate(inputs)— bulk createbulkUpdate(request)— bulk updategetQueryPage(pageId)— cursor pagination for querygetHistoryPage(entityId, pageId)— cursor pagination for history
5. Impact on Other Documents
Section titled “5. Impact on Other Documents”Path Correction
Section titled “Path Correction”The deployed endpoint path /v1/item/image-upload differs from the system
design assumption of /v1/item/item/<itemEId>/image-upload-url (TD-13). The
following documents have been updated:
goal.md— corrected: backend path, BFF route, deliverables, scope, constraints, and success criteria now reference/v1/item/image-upload(backend) and/api/image-upload(BFF)upload-component-backend-analysis.md— corrected: API functions layer, connection point table, PlantUML diagram, and hook examples now use the module-scoped path
The following documents in the system design section still reference the original entity-scoped path and should be reviewed in a future update:
- BFF specification — proxy route assumed at
POST /api/item/<itemEId>/image-upload-url - SPA specification — outbound interface table
The BFF route path (/api/...) is a design choice for arda-frontend-app and
does not need to mirror the backend path exactly. However, the BFF route
handler must call the correct backend path (/v1/item/image-upload).
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved