Internal API Contract — UI ↔ Backend (FE↔BE)

*Status:* v1.0 authoritative · Owner: Backend + Frontend (shared, CTO sign-off) · Last reviewed: 2026-06-26 · Review cadence: quarterly

This is the internal API: the Angular UI talking to its own Java backend (oneiot-ui / iotw).

It is POST-RPC and hand-kept — no codegen — so it must change here first, identically on both sides.

The frontend and backend are built by different teams against separate standards but meet at this one wire contract; both the Java and Angular standards link here.

For the outward-facing API consumed by other systems (northbound / public / integration), see the External API Contract — that’s a different surface (REST + OpenAPI), not this one.
contract-flow

The contract

Element Shape Owner of change

Envelope

ICollection<T> = { items: T[] }; paged lists use ICollectionWithPagination<T>

BE proposes

Pagination

{ pageNumbers, pageSize, sorts: [{ field, direction }] }

BE proposes

Error codes

numeric 90xx, e.g. 9024 (device not found), 9018 (domain denied) — BE returns, FE maps to a user message. This is the internal catalog; the External API has its own (100/20X) — don’t mix.

BE owns the list

URL style

POST-RPC /{controller}/{action}

shared

Identity

serial (string) · deviceId (number)

shared

API operations — verb, path & types per operation

This page is the internal FE↔BE surface: POST-RPC, not REST — the URL is /{controller}/{action} where action is a resource noun, and reads carry their filter/id in the request body (not the query string). The HTTP verb is fixed per kind of operation as below (reference: angular-ui ApiService
device.service.ts).

FT also has a northbound / public REST surface (ft-northbound-api, ft-service-api, ft-provision-api) with proper verbs + OpenAPI — that’s a different style; see Java standard §J12. Don’t apply this POST-RPC table to those.
Operation Verb Path (action) Request body → Response FE service method

List / query

POST

/{controller}/items

filter + { pageNumbers, pageSize, sorts }ICollectionWithPagination<T> (or ICollection<T> if unpaged)

getXxxList(payload)

Get by id

POST

/{controller}/<resource> (e.g. deviceInfo)

{ id }T

getXxx(id) / getXxxInfo(id)

Create

PUT

/{controller}/item

the new DTO → boolean / created id

addXxx(payload)

Update

PUT

/{controller}/<resource>

the changed DTO → boolean / updated

updateXxx(params) / setXxx(params)

Delete (bulk by id)

DELETE

/{controller}/items

{ ids: number[] } (in the body) → void

deleteXxxs(ids)

Sub-resource

POST

/{controller}/<resource>/<sub> (e.g. history/details)

{ id } or filter → ICollection<…​>

getXxx…​(…)

Rule Why / detail

Reads use POST (not GET)

filter/paging/id travel in the JSON body; keeps complex queries uniform

item (singular) vs items (plural)

singular = one entity (create / get-one); plural = a collection (list / bulk-delete)

Create and update both use PUT

FT convention — distinguish by action, not by verb (item create vs named-resource update)

DELETE carries ids in the body

bulk delete by { ids: […​] }, not a path id

action casing

camelCase resource noun (deviceInfo, accountInfo); nest sub-resources with /

FE method naming

getXxxList / getXxx(id) / addXxx / updateXxx·setXxx / deleteXxxs — verb says the operation

This is the hand-kept contract — the BE controller must expose exactly these verb+path pairs and the FE service must call them by these names. Don’t introduce REST-style GET /items/{id} on one side only.

How each side uses it

Backend (Java) Frontend (Angular)
  • Returns the envelope / pagination shape verbatim.

  • Logs deviceId / sessionId as key=value (J5).

  • Owns the error-code list.

  • Calls POST-RPC through a thin ApiService.

  • Maps error codes to user-facing messages in NgRx effects.

Changing the contract

  1. Edit this page first (and the matching Java §J12 HTTP-API section + the Angular FE↔BE checklist).

  2. Update both sides in the same change set — they must never drift.

  3. Because there is no codegen, a contract change is a coordinated cross-team PR, not a backend-only one.