rest-api
Technical notes on web development, DevOps, and AI integration.
12 articles
- 19:30backend
The Status Contract Lives in the Query, Not in a Parameter
Public organization detail now goes through GetActiveByID with the status clause inside the query: a missing row and a non-AKTIF row both answer 404.
TL;DR: Organization 17 is inactive. Public requests return 404. Admin requests return the full record.
#go#mysql#rest-api - 19:06security
The Masked Public Roster: Ship Names, Not Phone Numbers
Hiding sensitive columns in the UI is not protection. The real mask lives in the API layer, proven by negative E2E assertions.
TL;DR: The article describes a common flaw where the UI hides sensitive fields but the API still sends full records to the client, violating OWASP API3:2023 and Indonesia's data minimization rules. The fix strips fields server-side via a projection DTO containing only public data. A Playwright test enforces negative assertions, ensuring PII never reaches the DOM.
#privacy#security#playwright - 01:06backend
Reject Unknown Status Filters, Do Not Ignore Them
A characterization test demanded total = 3 for status=bogus, so the API answered 200 with every live row. One switch statement flipped it to fail-closed 400.
TL;DR: The endpoint silently ignored unrecognized status filters, returning all rows with 200 OK, and a green test had pinned that fail-open behavior as intended. A strict switch now separates empty string from invalid values, rejecting the latter with 400. Guidelines like AIP-160 agree, proving green tests don't guarantee a correct contract.
#golang#api#rest-api - 17:58backend
An Honest Contract for Batch Ingest Endpoints
One success number for a whole batch hides partial failure. Per-item created/updated/unchanged/rejected status makes it visible.
TL;DR: The new POST endpoint replaces a single batch status with per-item outcomes plus a summary so partial failures are visible. Guards cap payload size and item count, rate-limit per source, and fail closed with proper status codes including constant-time auth. Each item is marked created, updated, unchanged or rejected via a content hash, with an audit row for every request.
#fastapi#api#rest-api - 16:24backend
When a Feature Is Turned Off, Answer 404 Not 403
A disabled reviews feature answered 404 instead of 403. The HTTP spec actually backs that choice, and one cheap gate makes it practical.
TL;DR: Returning 403 for a disabled feature confirms the resource exists, inviting attackers to keep probing. Using 404 instead hides it completely, and a single service-layer flag can protect all related endpoints while the frontend hides UI via feature flags. For admin APIs though, 403 or 410 makes more sense since admins need to know the feature is just disabled.
#http#api#error-handling - 16:24database
Soft Delete Done Right: Predicates, RowsAffected, and Restore
Soft delete is not just DELETE swapped for UPDATE. Predicates on every query, a RowsAffected alarm, and a mirrored restore guard keep it honest.
TL;DR: Switching DELETE to soft delete with an UPDATE on deleted_at left deleted rows visible because SELECTs weren't filtered. Fixing it means adding deleted_at IS NULL to queries, checking RowsAffected to return 404, and mirroring logic for restores. You also need to invalidate cache on every state change, otherwise complexity and leaks quickly outweigh benefits.
#go#mysql#sql - 15:07backend
The Category Trash Can: One Endpoint and POST Restore
A soft delete API contract: the trashed query flag, POST restore, and a 404 that deliberately merges two conditions.
TL;DR: The categories list supports a trashed query param defaulting to false so existing clients keep seeing active items while new UIs can fetch soft-deleted ones. Restore is a POST endpoint returning 200 on success, 403 when out of scope, or a shared 404 for missing or not-trashed cases. It's a clean additive contract-first change that keeps compatibility without new endpoints.
#api#openapi#rest-api - 14:17devops
The document mirror moved offices: work items to Plane Pages
The document mirror moved off the kanban board and into Plane Pages, and the storage contract forced a full identity redesign.
TL;DR: The mirror moved from Work Items to Pages, which preserves HTML comments instead of sanitizing them, forcing a new identity design. Pages are flat with no folders, need full replacement on update, and require archiving before delete with verify-after-write. Sync now trusts content hashes, allows two-way edits only for meeting notes, and keeps library docs one-way with divergence alerts.
#plane#self-hosting#git - 23:25devops
Done on the tracker is a claim; the proof lives in Git
A card flipped to Done with nothing behind it. Now a work item only counts as done when its evidence hashes actually resolve in the repository.
TL;DR: A Done label means nothing without Git proof, so I stopped trusting tracker status alone. I built a gate that only allows Done when visible evidence lines with commit hashes and tags actually resolve in the repo. Strict rules like WIP limits, date handling, and separating docs keep the board honest and verifiable.
#plane#self-hosting#git