The browser calls a same-origin endpoint

Article pages send requests to the website's /api/session endpoints. Access tokens, refresh tokens, and the device key live in HttpOnly cookies, outside page-script access. The Next.js server selects the App API address and X-AppID from controlled configuration. A submitted form value cannot switch that configuration to another App.

The session proxy first calls /me to establish identity. If access credentials are absent or expired, an eligible request attempts refresh. The bookmark mutation runs after recovery succeeds. Temporary backend failures return a recoverable error, and a write that might already have executed is not automatically replayed after a failed response.

This ordering separates session recovery from the business mutation. During diagnosis, a failed identity check can be distinguished from an authorized request whose bookmark operation failed.

Duplicate and unauthorized bookmark changes

The App API checks publication state and App scope. User identity comes from authenticated context; submitting a user_id does not determine ownership. The persistence query ignores conflicts on the unique user/article relationship, so repeated saves retain one bookmark.

Removal is constrained by tenant, App, user, and article. Showing buttons only for the current user is an interface restriction; SQL conditions also restrict callers that construct requests directly. Test both repeated saves by one user and attempted removal by another.

Mobile and official Web use the App API; administration uses a separate Admin API. Both enter modular Go services backed by PostgreSQL.

The diagram describes logical calls. It does not specify the deployed process count or report measured throughput.

Authority at the API boundary

Ordinary users use /api/v1 to read published content and manage their bookmarks. Administration uses /admin-api/v1 to edit and publish. Token audiences are isolated; a website login confers no administration publishing permission.

Requests also carry tenant and App scope. Public content queries retain App, publication, and language conditions. Anonymous readability does not expose the whole database. Business code evaluates stable error codes while interfaces translate their descriptions.

The backend uses GoFrame, pgx, and sqlc. Transport converts requests, Application code coordinates use cases and transactions, and repositories execute queries. Modules are explicitly assembled at startup. Related operations within one service can be traced through transaction boundaries and call stacks; a separate process needs a load or release requirement that justifies it.

Extending a field across the applications

For a resolution note, decide its length, whether it is required, who can edit it, and how existing records behave. A stored field requires migration and SQL changes. An API field needs an OpenAPI definition. Administration and mobile need generated types, validation, and translated presentation.

Acceptance should include an authorized save, an unauthorized write, an older client omitting the field, and concurrent edits. On transaction failure, check for partial records or external notifications already sent. Typing successfully into the new form does not cover those conditions.

Locate a failure by its boundary

For 401, inspect credentials and session state. For 403, inspect authority. For 404, inspect the resource and the permitted access scope. An empty list calls for checking App, category filter, publication state, and language. A timeout should display an error instead of returning a plausible empty list.

Website content requests send Accept-Language, use a ten-second timeout, and validate HTTP results and response shape. Inspect the Web proxy, App API, and SQL separately. Source references include the App API contract, content queries, and backend blueprint.