Card 08/ 10
Database Rows Returned as the API
An endpoint that serialises a table row ships its column names, its internal flags and every future schema change to clients.
An endpoint that returns a database row, or an object mapped straight from one, makes the table the contract. Every column name, internal flag and secret in the row reaches clients, and every later change to the table becomes a breaking change to the API.
Picture a mobile app that stops showing customers' names overnight. A developer renamed the column full_name to display_name in a database migration. Nobody thought of a migration as an API change, but the API returned the row, so the field the app read was gone.
The symptom. Responses with snake_case column names, fields such as password_hash or deleted_at, and clients that break after database changes.
The rowthe table is the contract
{"user_id": 7, "full_name": "Ana Ruiz",
"password_hash": "$2b$12$...", "is_admin": 0,
"deleted_at": null, "internal_score": 0.82}Designedchosen for the client
{"id": "7", "name": "Ana Ruiz",
"role": "member"}Why it happens. Frameworks make it one line to serialise an entity, the object mapped to a table, and it works on the first day. The cost arrives later and lands on someone else.
What it costs. A leaked password hash an attacker can try to crack offline. Internal flags clients start to depend on. A database that can no longer be changed without coordinating every client. And on the way in, a client that sends "is_admin": 1 may find the server writes it, a flaw called mass assignment.
What to do. Define a response type for each resource, designed for the client, and map the entity to it explicitly. Define request types the same way, accepting only the fields a client may set. The mapping is a small cost paid once for a contract the database cannot break.
← → move through sections and cards